OLD | NEW |
---|---|
(Empty) | |
1 package com.google.dart.compiler.ast; | |
2 | |
3 import java.math.BigInteger; | |
4 import java.net.URI; | |
5 import java.util.ArrayList; | |
6 import java.util.HashMap; | |
7 import java.util.HashSet; | |
8 import java.util.List; | |
9 import java.util.Map; | |
10 import java.util.Set; | |
11 | |
12 import com.google.dart.compiler.common.SourceInfo; | |
13 import com.google.dart.compiler.CommandLineOptions.CompilerOptions; | |
14 import com.google.dart.compiler.CompilerConfiguration; | |
15 | |
16 /** | |
17 * CoverageInstrumenter contains specialized instrumenters to perform specific | |
18 * instrumentation for obtaining coverage of different program entities | |
19 */ | |
20 public class CoverageInstrumenter { | |
21 private List<BaseInstrumenter> instrumenters; | |
22 private static final String[] ignoredLibs = { "corelib", "corelib_impl", | |
23 "dom", "html", "htmlimpl", "base", "touch", "view", "utilslib", | |
24 "observable", "layout.dart", "unittest", "dartest" }; | |
25 | |
26 // Only createInstance should be used to instantiate this class | |
27 private CoverageInstrumenter() { | |
28 } | |
29 | |
30 /** | |
31 * Method to instantiate a CoverageInstrumenter containing multiple | |
32 * instrumenters created based on command line flags. | |
33 * | |
34 * @param config | |
35 * @return instance of this class | |
36 */ | |
37 public static CoverageInstrumenter createInstance( | |
38 CompilerConfiguration config) { | |
39 | |
40 CompilerOptions compilerOptions = config.getCompilerOptions(); | |
41 String coverageTypes = compilerOptions.getCoverageType(); | |
42 | |
43 CoverageInstrumenter instr = new CoverageInstrumenter(); | |
44 if (!"".equals(coverageTypes)) { | |
45 String outDir = compilerOptions.getWorkDirectory() | |
46 .getAbsolutePath(); | |
47 instr.createInstrumenters(coverageTypes, outDir); | |
48 } | |
49 return instr; | |
50 } | |
51 | |
52 /** | |
53 * Performs instrumentation of all classes | |
54 * | |
55 * @param libraries | |
56 */ | |
57 public void process(Map<URI, LibraryUnit> libraries) { | |
58 | |
59 if (instrumenters == null) { | |
60 return; | |
61 } | |
62 | |
63 // Instrument all dart units | |
64 for (LibraryUnit lib : libraries.values()) { | |
65 for (DartUnit unit : lib.getUnits()) { | |
66 exec(unit); | |
67 } | |
68 } | |
69 | |
70 // Populate totals and initialize coverage | |
71 for (LibraryUnit lib : libraries.values()) { | |
72 for (DartUnit unit : lib.getUnits()) { | |
73 init(unit); | |
74 } | |
75 } | |
76 } | |
77 | |
78 /** | |
79 * Runs all instrumenters on a particular Dart unit | |
80 * | |
81 * @param unit | |
82 */ | |
83 private void exec(DartUnit unit) { | |
84 if (isIgnored(unit)) { | |
85 return; | |
86 } | |
87 System.out.println("Instrumenting " + unit.getSourceName() + ", lib:" | |
zundel
2011/12/16 20:49:05
this looks like debugging... for a real dart prog
| |
88 + unit.getLibrary().getName()); | |
89 for (BaseInstrumenter instrumenter : instrumenters) { | |
90 instrumenter.accept(unit); | |
91 } | |
92 } | |
93 | |
94 /** | |
95 * Ignores a Dart unit based on the library it belongs to | |
96 * | |
97 * @param unit | |
98 * @return true is the unit should be ignored | |
99 */ | |
100 private boolean isIgnored(DartUnit unit) { | |
101 LibraryUnit lu = unit.getLibrary(); | |
102 if (lu != null) { | |
103 String libName = lu.getName(); | |
104 for (String ignoredLib : ignoredLibs) { | |
105 if (ignoredLib.equals(libName)) { | |
106 return true; | |
107 } | |
108 } | |
109 } | |
110 return false; | |
111 } | |
112 | |
113 /** | |
114 * Runs the TotalSettingInstrumenter on the supplied Dart unit, which adds | |
115 * totals of all coverable program entities | |
116 * | |
117 * @param unit | |
118 */ | |
119 private void init(DartUnit unit) { | |
120 if (isIgnored(unit)) { | |
121 return; | |
122 } | |
123 new TotalSettingInstrumenter().accept(unit); | |
124 } | |
125 | |
126 /** | |
127 * Factory method to create instrumenters based on supplied command line | |
128 * configuration | |
129 * | |
130 * @param coverageTypes | |
131 * @param outDir | |
132 */ | |
133 private void createInstrumenters(String coverageTypes, String outDir) { | |
134 BaseInstrumenter.setOutDir(outDir); | |
135 instrumenters = new ArrayList<BaseInstrumenter>(); | |
136 if (coverageTypes.length() > 0) { | |
137 for (String covType : coverageTypes.split(",")) { | |
138 if ("all".equals(covType)) { | |
zundel
2011/12/16 20:49:05
instead of inlined strings, I'd make constants out
| |
139 instrumenters.add(new StatementInstrumenter()); | |
140 instrumenters.add(new FunctionInstrumenter()); | |
141 instrumenters.add(new BranchInstrumenter()); | |
142 } | |
143 if ("statement".equals(covType)) { | |
144 instrumenters.add(new StatementInstrumenter()); | |
zundel
2011/12/16 20:49:05
is it going to cause problems if you add the same
| |
145 } | |
146 if ("function".equals(covType)) { | |
147 instrumenters.add(new FunctionInstrumenter()); | |
148 } | |
149 if ("branch".equals(covType)) { | |
150 instrumenters.add(new BranchInstrumenter()); | |
151 } | |
152 } | |
153 } | |
154 } | |
155 | |
156 /** | |
157 * The Base Instrumenter class that provides some common functionalities to | |
158 * all Instrumenters | |
159 */ | |
160 private static class BaseInstrumenter extends DartModVisitor { | |
zundel
2011/12/16 20:49:05
FYI, we've been trying to replace instances of Dar
| |
161 private static String outputDir; | |
162 protected DartUnit currentUnit; | |
163 protected static Set<String> unitsVisited = new HashSet<String>(); | |
164 protected static Map<String, Integer> numFunctionsMap = | |
165 new HashMap<String, Integer>(), | |
zundel
2011/12/16 20:49:05
style nit: don't use the comma for followon declar
| |
166 numStatementsMap = new HashMap<String, Integer>(), | |
167 numBranchesMap = new HashMap<String, Integer>(); | |
168 | |
169 public static void setOutDir(String outDir) { | |
170 outputDir = outDir; | |
171 } | |
172 | |
173 @Override | |
174 public boolean visit(DartUnit x, DartContext ctx) { | |
175 currentUnit = x; | |
176 unitsVisited.add(x.getSourceName()); | |
177 return super.visit(x, ctx); | |
178 } | |
179 | |
180 /** | |
181 * Prepend a function call to a Dart block, which is a body of a | |
182 * function. | |
183 * | |
184 * @param oldBody | |
185 * @param functionStmt | |
186 * @return | |
187 */ | |
188 protected DartBlock prependToFnBody(DartBlock oldBody, | |
189 DartExprStmt functionStmt) { | |
190 List<DartStatement> blockStmts = new ArrayList<DartStatement>(); | |
191 blockStmts.add(functionStmt); | |
192 if (oldBody != null) { | |
193 List<DartStatement> stmts = oldBody.getStatements(); | |
194 if (stmts != null) { | |
195 blockStmts.addAll(stmts); | |
196 } | |
197 } | |
198 DartBlock newDB = new DartBlock(blockStmts); | |
199 if (oldBody != null) { | |
200 newDB.setSourceInfo(oldBody.getSourceInfo()); | |
201 } | |
202 return newDB; | |
203 } | |
204 | |
205 protected void replaceFunction(DartContext ctx, DartFunction x, | |
206 DartBlock newFnBody) { | |
207 DartFunction newFn = new DartFunction(x.getParams(), newFnBody, | |
208 x.getReturnTypeNode()); | |
209 newFn.setSourceInfo(x.getSourceInfo()); | |
210 ctx.replaceMe(newFn); | |
211 } | |
212 | |
213 protected DartExprStmt createFunctionCall(String functionName, | |
214 List<DartExpression> args, DartNode oldNode) { | |
215 DartIdentifier funcName = new DartIdentifier(functionName); | |
216 DartUnqualifiedInvocation funcInvocation = new DartUnqualifiedInvocation( | |
217 funcName, args); | |
218 DartExprStmt functionStmt = new DartExprStmt(funcInvocation); | |
219 if (oldNode != null) { | |
220 SourceInfo sourceInfo = oldNode.getSourceInfo(); | |
221 setInstrumentedSourceInfo(functionStmt, sourceInfo); | |
222 setInstrumentedSourceInfo(funcInvocation, sourceInfo); | |
223 setInstrumentedSourceInfo(funcName, sourceInfo); | |
224 for (DartExpression arg : args) { | |
225 setInstrumentedSourceInfo(arg, sourceInfo); | |
226 } | |
227 } | |
228 return functionStmt; | |
229 } | |
230 | |
231 void setInstrumentedSourceInfo(DartNode node, SourceInfo info) { | |
232 node.setInstrumentedNode(true); | |
233 node.setSourceInfo(info); | |
234 } | |
235 } | |
236 | |
237 /** | |
238 * TotalSettingInstrumenter instruments the main function to initialize | |
239 * coverage variables and set totals for covered entities | |
240 */ | |
241 private class TotalSettingInstrumenter extends BaseInstrumenter { | |
242 | |
243 @Override | |
244 public boolean visit(DartFunction x, DartContext ctx) { | |
245 DartNode parent = x.getParent(); | |
246 if (parent instanceof DartMethodDefinition) { | |
247 String funcName = ((DartMethodDefinition) parent).getName() | |
248 .toString(); | |
249 if ("main".equals(funcName)) { | |
250 DartBlock newFnBody = x.getBody(); | |
251 | |
252 for (String unitName : unitsVisited) { | |
253 int numFunctions = nullCheckingUnBox(numFunctionsMap | |
254 .get(unitName)); | |
255 int numStatements = nullCheckingUnBox(numStatementsMap | |
256 .get(unitName)); | |
257 int numBranches = nullCheckingUnBox(numBranchesMap | |
258 .get(unitName)); | |
259 List<DartExpression> args = new ArrayList<DartExpression>(); | |
260 args.add(DartStringLiteral.get(unitName)); | |
261 args.add(DartIntegerLiteral.get(BigInteger | |
262 .valueOf(numFunctions))); | |
263 args.add(DartIntegerLiteral.get(BigInteger | |
264 .valueOf(numStatements))); | |
265 args.add(DartIntegerLiteral.get(BigInteger | |
266 .valueOf(numBranches))); | |
267 DartExprStmt callCovTotals = createFunctionCall( | |
268 "setCoverageTotals", args, newFnBody); | |
269 callCovTotals.setInstrumentedNode(true); | |
270 newFnBody = prependToFnBody(newFnBody, callCovTotals); | |
271 } | |
272 | |
273 replaceFunction(ctx, x, newFnBody); | |
274 } | |
275 } | |
276 return super.visit(x, ctx); | |
277 } | |
278 | |
279 int nullCheckingUnBox(Integer value) { | |
280 int ret = 0; | |
281 if (value != null) { | |
282 ret = value.intValue(); | |
283 } | |
284 return ret; | |
285 } | |
286 } | |
287 | |
288 /** | |
289 * FunctionInstrumenter adds instrumentation to track function coverage | |
290 */ | |
291 private class FunctionInstrumenter extends BaseInstrumenter { | |
292 | |
293 private int numFunctions; | |
294 | |
295 /** | |
296 * This visitor method transforms: | |
297 * | |
298 * myFunction(){ stmts; ... } | |
299 * | |
300 * to: | |
301 * | |
302 * myFunction(){ coverFunction('unit.dart', 'myFunction'); stmts; ... } | |
zundel
2011/12/16 20:49:05
You aren't covering closures here. Is that intenti
| |
303 */ | |
304 @Override | |
305 public boolean visit(DartFunction x, DartContext ctx) { | |
306 DartNode parent = x.getParent(); | |
307 if (parent instanceof DartMethodDefinition) { | |
308 String funcName = ((DartMethodDefinition) parent).getName() | |
309 .toString(); | |
310 DartBlock oldBody = x.getBody(); | |
311 List<DartExpression> covArgs = new ArrayList<DartExpression>(); | |
312 covArgs.add(DartStringLiteral.get(currentUnit.getSourceName())); | |
313 covArgs.add(DartStringLiteral.get(funcName)); | |
314 DartExprStmt callCovFunc = createFunctionCall("coverFunction", | |
315 covArgs, oldBody); | |
316 DartBlock newFnBody = prependToFnBody(oldBody, callCovFunc); | |
317 replaceFunction(ctx, x, newFnBody); | |
318 numFunctions++; | |
319 } | |
320 return super.visit(x, ctx); | |
321 } | |
322 | |
323 /** | |
324 * Initialize function counter | |
325 */ | |
326 @Override | |
327 public boolean visit(DartUnit x, DartContext ctx) { | |
328 numFunctions = 0; | |
329 return super.visit(x, ctx); | |
330 } | |
331 | |
332 /** | |
333 * Populate number of functions for current unit | |
334 */ | |
335 @Override | |
336 public void endVisit(DartUnit x, DartContext ctx) { | |
337 numFunctionsMap.put(currentUnit.getSourceName(), numFunctions); | |
338 super.endVisit(x, ctx); | |
339 } | |
340 | |
341 } | |
342 | |
343 /** | |
344 * StatementInstrumenter adds instrumentation before each statement to | |
345 * capture runtime coverage of that statement | |
346 */ | |
347 private class StatementInstrumenter extends BaseInstrumenter { | |
348 private int numStatements; | |
349 | |
350 /** | |
351 * Initialize statement counter | |
352 */ | |
353 @Override | |
354 public boolean visit(DartUnit x, DartContext ctx) { | |
355 numStatements = 0; | |
356 return super.visit(x, ctx); | |
357 } | |
358 | |
359 /** | |
360 * Populate number of statements for current unit | |
361 */ | |
362 @Override | |
363 public void endVisit(DartUnit x, DartContext ctx) { | |
364 numStatementsMap.put(currentUnit.getSourceName(), numStatements); | |
365 super.endVisit(x, ctx); | |
366 } | |
367 | |
368 @Override | |
369 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
| |
370 List<DartStatement> newStmts = new ArrayList<DartStatement>(); | |
371 for (DartStatement stmt : x.getStatements()) { | |
372 if (!stmt.isInstrumentedNode()) { | |
373 List<DartExpression> covArgs = new ArrayList<DartExpression>(); | |
374 covArgs.add(DartStringLiteral.get(currentUnit | |
375 .getSourceName())); | |
376 covArgs.add(DartIntegerLiteral.get(BigInteger.valueOf(stmt | |
377 .getSourceLine()))); | |
378 DartExprStmt callCoverStmt = createFunctionCall( | |
379 "coverStatement", covArgs, stmt); | |
380 newStmts.add(callCoverStmt); | |
381 } | |
382 newStmts.add(stmt); | |
383 numStatements++; | |
384 } | |
385 DartBlock newBlock = new DartBlock(newStmts); | |
386 ctx.replaceMe(newBlock); | |
387 return super.visit(x, ctx); | |
388 } | |
389 | |
390 } | |
391 | |
392 /** | |
393 * BranchInstrumenter captures runtime execution of each branch in the | |
394 * program by adding instrumentation at each branch point | |
395 */ | |
396 private class BranchInstrumenter extends BaseInstrumenter { | |
397 private int numBranches; | |
398 | |
399 @Override | |
400 public boolean visit(DartUnit x, DartContext ctx) { | |
401 numBranches = 0; | |
402 return super.visit(x, ctx); | |
403 } | |
404 | |
405 @Override | |
406 public void endVisit(DartUnit x, DartContext ctx) { | |
407 numBranchesMap.put(currentUnit.getSourceName(), numBranches); | |
408 super.endVisit(x, ctx); | |
409 } | |
410 | |
411 /** | |
412 * Post-order instrumentation is needed to handle nested ifs and | |
413 * synthetic else | |
414 */ | |
415 @Override | |
416 public void endVisit(DartIfStatement x, DartContext ctx) { | |
zundel
2011/12/16 20:49:05
same comment as above - I'd appreciate seeing how
| |
417 DartIfStatement newIfStmt; | |
418 DartStatement thenStmt = x.getThenStatement(); | |
419 DartStatement elseStmt = x.getElseStatement(); | |
420 | |
421 List<DartStatement> newThen = doCallCoverBranch(thenStmt); | |
422 numBranches++; | |
423 if (thenStmt instanceof DartBlock) { | |
424 List<DartStatement> stmts = ((DartBlock) thenStmt) | |
425 .getStatements(); | |
426 if (stmts != null) { | |
427 newThen.addAll(stmts); | |
428 } | |
429 } else { | |
430 newThen.add(thenStmt); | |
431 } | |
432 | |
433 if (elseStmt instanceof DartIfStatement) { | |
434 newIfStmt = new DartIfStatement(x.getCondition(), | |
435 new DartBlock(newThen), elseStmt); | |
436 } else { | |
437 List<DartStatement> newElse; | |
438 if (elseStmt != null) { | |
439 newElse = doCallCoverBranch(elseStmt); | |
440 if (elseStmt instanceof DartBlock) { | |
441 List<DartStatement> stmts = ((DartBlock) elseStmt) | |
442 .getStatements(); | |
443 if (stmts != null) { | |
444 newElse.addAll(stmts); | |
445 } | |
446 } else { | |
447 newElse.add(elseStmt); | |
448 } | |
449 } else { | |
450 // Although else block is missing, there is a branch which | |
451 // doesn't cover the IfStatement | |
452 // So, we need to add a synthetic else block to track that | |
453 newElse = doCallCoverBranch(x); | |
454 } | |
455 newIfStmt = new DartIfStatement(x.getCondition(), | |
456 new DartBlock(newThen), new DartBlock(newElse)); | |
457 numBranches++; | |
458 } | |
459 newIfStmt.setSourceInfo(x.getSourceInfo()); | |
460 ctx.replaceMe(newIfStmt); | |
461 super.endVisit(x, ctx); | |
462 } | |
463 | |
464 /** | |
465 * This method instruments all non-empty switch case blocks and the | |
466 * default block. It inserts a synthetic default statement if one | |
467 * doesn't exist. | |
468 * | |
469 * For tracking branch on empty cases, this logic won't work directly | |
470 * since instrumenting empty cases will throw a FallThroughError at | |
471 * runtime. For achieving this, the switch needs to be transformed into | |
472 * an if. Its slightly complicated but here is what I think the | |
473 * transformation should be: | |
474 * | |
475 * switch(expr) { | |
476 * case 'a': // empty switch case | |
477 * case 'b': do1(); break; | |
zundel
2011/12/16 20:49:05
I didn't think you needed a break stmt in dart.
| |
478 * case 'c': do2(); break; | |
479 * default: doDefault(); | |
480 * } | |
481 * | |
482 * should be transformed to: | |
483 * | |
484 * var tmp = expr; | |
485 * if( (tmp == 'a' && coverBranch(..)) || (tmp == 'b' && | |
486 * coverBranch(..))) { | |
487 * do1(); | |
488 * } else if (tmp == 'a' && coverBranch(..)) { | |
489 * do2(); | |
490 * } else { | |
491 * coverBranch(..); | |
492 * doDefault(); | |
493 * } | |
494 * | |
495 */ | |
496 @Override | |
497 public void endVisit(DartSwitchStatement x, DartContext ctx) { | |
498 List<DartSwitchMember> newSwitchMembers = | |
499 new ArrayList<DartSwitchMember>(); | |
500 boolean hasDefault = false; | |
501 for (DartSwitchMember swMember : x.getMembers()) { | |
502 List<DartStatement> stmts = doCallCoverBranch(swMember); | |
503 List<DartStatement> oldStmts = swMember.getStatements(); | |
504 if (oldStmts == null || oldStmts.size() == 0) { | |
505 newSwitchMembers.add(swMember); | |
506 continue; // Ignore empty cases | |
507 } else { | |
508 stmts.addAll(oldStmts); | |
509 } | |
510 | |
511 assert (swMember instanceof DartCase || | |
512 swMember instanceof DartDefault); | |
513 | |
514 DartSwitchMember newMember; | |
515 if (swMember instanceof DartCase) { | |
516 newMember = new DartCase(((DartCase) swMember).getExpr(), | |
517 swMember.getLabel(), stmts); | |
518 } else { | |
519 hasDefault = true; | |
520 newMember = new DartDefault(swMember.getLabel(), stmts); | |
521 } | |
522 setInstrumentedSourceInfo(newMember, swMember); | |
523 newSwitchMembers.add(newMember); | |
524 numBranches++; | |
525 } | |
526 | |
527 if (!hasDefault) { | |
528 List<DartStatement> statements = doCallCoverBranch(x); | |
529 DartSwitchMember defaultMember = new DartDefault(null, | |
530 statements); | |
531 newSwitchMembers.add(defaultMember); | |
532 numBranches++; | |
533 } | |
534 | |
535 DartSwitchStatement newSwitch = new DartSwitchStatement( | |
536 x.getExpression(), newSwitchMembers); | |
537 newSwitch.setSourceInfo(x.getSourceInfo()); | |
538 | |
539 ctx.replaceMe(newSwitch); | |
540 super.visit(x, ctx); | |
541 } | |
542 | |
543 @Override | |
544 public void endVisit(DartTryStatement x, DartContext ctx) { | |
545 DartBlock tryBlock = x.getTryBlock(), finallyBlock = x | |
546 .getFinallyBlock(); | |
547 List<DartCatchBlock> catchBlocks = x.getCatchBlocks(); | |
548 | |
549 List<DartStatement> tryStmts = doCallCoverBranch(tryBlock); | |
550 if (tryBlock != null && tryBlock.getStatements() != null) { | |
551 tryStmts.addAll(tryBlock.getStatements()); | |
552 } | |
553 DartBlock newTryBlock = new DartBlock(tryStmts); | |
554 setInstrumentedSourceInfo(newTryBlock, tryBlock.getSourceInfo()); | |
555 numBranches++; | |
556 | |
557 List<DartCatchBlock> newCatchBlocks = null; | |
558 if (catchBlocks != null) { | |
559 newCatchBlocks = new ArrayList<DartCatchBlock>(); | |
560 for (DartCatchBlock cBlock : catchBlocks) { | |
561 DartBlock oldBlock = cBlock.getBlock(); | |
562 | |
563 List<DartStatement> cBlockStmts = doCallCoverBranch(cBlock); | |
564 if (oldBlock != null && oldBlock.getStatements() != null) { | |
565 cBlockStmts.addAll(oldBlock.getStatements()); | |
566 } | |
567 DartBlock newBlock = new DartBlock(cBlockStmts); | |
568 setInstrumentedSourceInfo(newBlock, | |
569 oldBlock.getSourceInfo()); | |
570 | |
571 DartCatchBlock newCatchBlock = new DartCatchBlock(newBlock, | |
572 cBlock.getException(), cBlock.getStackTrace()); | |
573 setInstrumentedSourceInfo(newCatchBlock, | |
574 cBlock.getSourceInfo()); | |
575 | |
576 newCatchBlocks.add(newCatchBlock); | |
577 numBranches++; | |
578 } | |
579 } | |
580 | |
581 DartBlock newFinallyBlock = null; | |
582 if(finallyBlock != null){ | |
583 List<DartStatement> finallyStmts = doCallCoverBranch(finallyBlock); | |
584 if(finallyBlock.getStatements() != null){ | |
585 finallyStmts.addAll(finallyBlock.getStatements()); | |
586 } | |
587 newFinallyBlock = new DartBlock(finallyStmts); | |
588 setInstrumentedSourceInfo(newFinallyBlock, | |
589 finallyBlock.getSourceInfo()); | |
590 } else { | |
591 List<DartStatement> finallyStmts = doCallCoverBranch(x); | |
592 newFinallyBlock = new DartBlock(finallyStmts); | |
593 setInstrumentedSourceInfo(newFinallyBlock, x.getSourceInfo()); | |
594 } | |
595 numBranches++; | |
596 | |
597 DartTryStatement newTry = new DartTryStatement(tryBlock, | |
598 newCatchBlocks, newFinallyBlock); | |
599 ctx.replaceMe(newTry); | |
600 super.endVisit(x, ctx); | |
601 } | |
602 | |
603 /** | |
604 * Rewrite all loops in this block | |
605 */ | |
606 @Override | |
607 public void endVisit(DartBlock x, DartContext ctx) { | |
608 List<DartStatement> blockStmts = new ArrayList<DartStatement>(); | |
609 for(DartStatement stmt : x.getStatements()) { | |
610 if(stmt instanceof DartForInStatement) { | |
611 doLoopBefore(stmt, blockStmts); | |
612 | |
613 DartForInStatement oldForIn = (DartForInStatement) stmt; | |
614 DartStatement setup = null; | |
615 if(oldForIn.introducesVariable()){ | |
616 setup = oldForIn.getVariableStatement(); | |
617 } else { | |
618 setup = new DartExprStmt(oldForIn.getIdentifier()); | |
619 } | |
620 DartBlock body = doLoopInside(stmt, oldForIn.getBody()); | |
621 DartForInStatement newForIn = | |
622 new DartForInStatement(setup, oldForIn.getIterable(), body); | |
623 setInstrumentedSourceInfo(newForIn, oldForIn.getSourceInfo()); | |
624 | |
625 doLoopAfter(stmt, blockStmts); | |
626 numBranches+=2; | |
627 } else if (stmt instanceof DartForStatement) { | |
628 doLoopBefore(stmt, blockStmts); | |
629 | |
630 DartForStatement oldFor = (DartForStatement) stmt; | |
631 DartBlock body = doLoopInside(stmt, oldFor.getBody()); | |
632 DartForStatement newFor = new DartForStatement(oldFor.getInit(), | |
633 oldFor.getCondition(), oldFor.getIncrement(), body); | |
634 setInstrumentedSourceInfo(newFor, oldFor.getSourceInfo()); | |
635 | |
636 doLoopAfter(stmt, blockStmts); | |
637 numBranches+=2; | |
638 } else if (stmt instanceof DartWhileStatement) { | |
639 doLoopBefore(stmt, blockStmts); | |
640 | |
641 DartWhileStatement oldWhile = (DartWhileStatement) stmt; | |
642 DartBlock body = doLoopInside(stmt, oldWhile.getBody()); | |
643 DartWhileStatement newWhile = | |
644 new DartWhileStatement(oldWhile.getCondition(), body); | |
645 setInstrumentedSourceInfo(newWhile, oldWhile.getSourceInfo()); | |
646 | |
647 doLoopAfter(stmt, blockStmts); | |
648 numBranches+=2; | |
649 } else if (stmt instanceof DartDoWhileStatement) { | |
650 doLoopBefore(stmt, blockStmts); | |
651 | |
652 DartDoWhileStatement oldDoWhile = (DartDoWhileStatement) stmt; | |
653 DartBlock body = doLoopInside(stmt, oldDoWhile.getBody()); | |
654 DartDoWhileStatement newDoWhile = | |
655 new DartDoWhileStatement(oldDoWhile.getCondition(), body); | |
656 setInstrumentedSourceInfo(newDoWhile, oldDoWhile.getSourceInfo()); | |
657 | |
658 doLoopAfter(stmt, blockStmts); | |
659 numBranches+=2; | |
660 } else { | |
661 blockStmts.add(stmt); | |
662 } | |
663 } | |
664 | |
665 super.visit(x, ctx); | |
666 } | |
667 | |
668 /** | |
669 * Adds function call to track coverage of a branch represented by | |
670 * blockNode | |
671 * | |
672 * @param blockNode | |
673 * @return | |
674 */ | |
675 private List<DartStatement> doCallCoverBranch(DartNode blockNode) { | |
676 List<DartStatement> newBlockStmts = new ArrayList<DartStatement>(); | |
677 List<DartExpression> covArgs = makeCoverageArgs(blockNode); | |
678 DartExprStmt callCoverBranch = createFunctionCall("coverBranch", | |
679 covArgs, blockNode); | |
680 newBlockStmts.add(callCoverBranch); | |
681 setInstrumentedSourceInfo(callCoverBranch, | |
682 blockNode.getSourceInfo()); | |
683 return newBlockStmts; | |
684 } | |
685 | |
686 private void doLoopBefore(DartNode loopNode, List<DartStatement> stmts) { | |
687 List<DartExpression> args = makeCoverageArgs(loopNode); | |
688 DartExprStmt callLoopBefore = | |
689 createFunctionCall("loopBranchBefore", args, loopNode); | |
690 stmts.add(callLoopBefore); | |
691 } | |
692 | |
693 private void doLoopAfter(DartNode loopNode, List<DartStatement> stmts) { | |
694 List<DartExpression> args = makeCoverageArgs(loopNode); | |
695 args.add(DartIntegerLiteral.get(BigInteger.valueOf( | |
696 loopNode.getSourceStart() + loopNode.getSourceLength()))); | |
697 DartExprStmt callLoopAfter = | |
698 createFunctionCall("coverLoopBranch", args, loopNode); | |
699 stmts.add(callLoopAfter); | |
700 } | |
701 | |
702 private DartBlock doLoopInside(DartNode loopNode, DartStatement body) { | |
703 List<DartStatement> newBlockStmts = new ArrayList<DartStatement>(); | |
704 List<DartExpression> covArgs = makeCoverageArgs(loopNode); | |
705 DartExprStmt callCoverBranch = createFunctionCall("loopBranchInside", | |
706 covArgs, loopNode); | |
707 newBlockStmts.add(callCoverBranch); | |
708 setInstrumentedSourceInfo(callCoverBranch, | |
709 loopNode.getSourceInfo()); | |
710 | |
711 if(body instanceof DartBlock) { | |
712 newBlockStmts.addAll(((DartBlock) body).getStatements()); | |
713 } else { | |
714 newBlockStmts.add(body); | |
715 } | |
716 | |
717 return new DartBlock(newBlockStmts); | |
718 } | |
719 private List<DartExpression> makeCoverageArgs(DartNode blockNode) { | |
720 List<DartExpression> covArgs = new ArrayList<DartExpression>(); | |
721 covArgs.add(DartStringLiteral.get(currentUnit.getSourceName())); | |
722 covArgs.add(DartIntegerLiteral.get(BigInteger.valueOf(blockNode | |
723 .getSourceLine()))); | |
724 covArgs.add(DartIntegerLiteral.get(BigInteger.valueOf(blockNode | |
725 .getSourceStart()))); | |
726 return covArgs; | |
727 } | |
728 } | |
729 | |
730 } | |
OLD | NEW |