1414
1515package com .google .testing .coverage ;
1616
17- import static java .util .Comparator .comparing ;
1817
1918import java .util .ArrayList ;
19+ import java .util .Collection ;
2020import java .util .HashMap ;
2121import java .util .HashSet ;
22+ import java .util .Iterator ;
2223import java .util .List ;
2324import java .util .Map ;
24- import java .util .Set ;
2525import java .util .TreeMap ;
2626import org .jacoco .core .internal .analysis .filter .IFilter ;
2727import org .jacoco .core .internal .analysis .filter .IFilterContext ;
2828import org .jacoco .core .internal .analysis .filter .IFilterOutput ;
29+ import org .jacoco .core .internal .analysis .filter .Replacements ;
30+ import org .jacoco .core .internal .analysis .filter .Replacements .InstructionBranch ;
2931import org .jacoco .core .internal .flow .IFrame ;
3032import org .jacoco .core .internal .flow .LabelInfo ;
3133import org .jacoco .core .internal .flow .MethodProbesVisitor ;
@@ -68,15 +70,13 @@ public class MethodProbesMapper extends MethodProbesVisitor implements IFilterOu
6870 private List <Label > currentLabels = new ArrayList <>();
6971 private AbstractInsnNode currentInstructionNode = null ;
7072 private final Map <AbstractInsnNode , Instruction > instructionMap = new HashMap <>();
71- private int instructionNodeIndex = 0 ;
72- private final Map <AbstractInsnNode , Integer > instructionNodeIndexMap = new HashMap <>();
7373
7474 // Filtering
7575 private final IFilter filter ;
7676 private final IFilterContext filterContext ;
7777 private final HashSet <AbstractInsnNode > ignored = new HashSet <>();
7878 private final Map <AbstractInsnNode , AbstractInsnNode > unioned = new HashMap <>();
79- private final Map <AbstractInsnNode , Set < AbstractInsnNode > > branchReplacements = new HashMap <>();
79+ private final Map <AbstractInsnNode , Replacements > branchReplacements = new HashMap <>();
8080
8181 // Result
8282 private Map <Integer , BranchExp > lineToBranchExp = new TreeMap <>();
@@ -89,7 +89,7 @@ public Map<Integer, BranchExp> result() {
8989 //
9090 // These values are built up during the visitor methods. They will be used to compute
9191 // the final results.
92- private final List < Instruction > instructions = new ArrayList <> ();
92+ private final InstructionSet instructions = new InstructionSet ();
9393 private final List <Jump > jumps = new ArrayList <>();
9494 private final List <Instruction > probedInstructions = new ArrayList <>();
9595 private final Map <Label , Instruction > labelToInsn = new HashMap <>();
@@ -106,7 +106,9 @@ public void accept(MethodNode methodNode, MethodVisitor methodVisitor) {
106106 currentInstructionNode = i ;
107107 i .accept (methodVisitor );
108108 }
109- filter .filter (methodNode , filterContext , this );
109+ if (filter != null ) {
110+ filter .filter (methodNode , filterContext , this );
111+ }
110112 methodVisitor .visitEnd ();
111113 }
112114
@@ -124,8 +126,6 @@ private void visitInsn() {
124126 currentLabels .clear (); // Update states
125127 lastInstruction = instruction ;
126128 instructionMap .put (currentInstructionNode , instruction );
127- instructionNodeIndexMap .put (currentInstructionNode , instructionNodeIndex );
128- instructionNodeIndex ++;
129129 }
130130
131131 // Plain visitors: called from adapter when no probe is needed
@@ -325,17 +325,26 @@ public void visitEnd() {
325325 }
326326
327327 // Handle branch replacements
328- for (Map .Entry <AbstractInsnNode , Set <AbstractInsnNode >> entry : branchReplacements .entrySet ()) {
329- // The replacement set is not ordered deterministically and we require it to be so to be able
330- // to merge multiple coverage reports later on. We use the order in which we encountered
331- // nodes to determine the order of branches for the new BranchExp.
332- ArrayList <AbstractInsnNode > replacements = new ArrayList <>(entry .getValue ());
333- replacements .sort (comparing (instructionNodeIndexMap ::get ));
334- BranchExp newBranch = new BranchExp (new ArrayList <>());
335- for (AbstractInsnNode replacement : replacements ) {
336- newBranch .add (instructionMap .get (replacement ).branchExp );
328+ for (Map .Entry <AbstractInsnNode , Replacements > entry : branchReplacements .entrySet ()) {
329+ BranchExp newBranchExp = BranchExp .initializeEmptyBranches ();
330+ int branchIndex = 0 ;
331+ for (Collection <InstructionBranch > replacements : entry .getValue ().values ()) {
332+ BranchExp subExp = BranchExp .initializeEmptyBranches ();
333+ int subBranchIndex = 0 ;
334+ for (InstructionBranch replacement : replacements ) {
335+ BranchExp branchExp = instructionMap .get (replacement .instruction ).branchExp ;
336+ subExp .setBranchAtIndex (subBranchIndex , branchExp .getBranchAtIndex (replacement .branch ));
337+ subBranchIndex ++;
338+ }
339+ newBranchExp .setBranchAtIndex (branchIndex , subExp );
340+ branchIndex ++;
337341 }
338- instructionMap .get (entry .getKey ()).branchExp = newBranch ;
342+ Instruction oldInsn = instructionMap .get (entry .getKey ());
343+ Instruction newInsn = new Instruction (oldInsn .line );
344+ newInsn .logicalBranches = branchIndex ;
345+ newInsn .branchExp = newBranchExp ;
346+ instructionMap .put (entry .getKey (), newInsn );
347+ instructions .replace (oldInsn , newInsn );
339348 }
340349
341350 HashSet <Instruction > ignoredInstructions = new HashSet <>();
@@ -390,8 +399,8 @@ public void merge(AbstractInsnNode i1, AbstractInsnNode i2) {
390399 }
391400
392401 @ Override
393- public void replaceBranches (AbstractInsnNode source , Set < AbstractInsnNode > newTargets ) {
394- branchReplacements .put (source , newTargets );
402+ public void replaceBranches (AbstractInsnNode source , Replacements replacements ) {
403+ branchReplacements .put (source , replacements );
395404 }
396405
397406 private AbstractInsnNode findRepresentative (AbstractInsnNode node ) {
@@ -471,4 +480,33 @@ static void wireBranchPredecessors(Instruction root) {
471480 }
472481 }
473482 }
483+
484+ /**
485+ * Permit efficient replacement of one instruction with another while preserving original
486+ * insertion order. A replacement instruction takes the place of the old instruction for iteration
487+ * order.
488+ */
489+ private static class InstructionSet implements Iterable <Instruction > {
490+
491+ private final List <Instruction > instructions = new ArrayList <>();
492+
493+ private final Map <Instruction , Integer > instructionIndex = new HashMap <>();
494+
495+ void add (Instruction instruction ) {
496+ instructionIndex .put (instruction , instructions .size ());
497+ instructions .add (instruction );
498+ }
499+
500+ void replace (Instruction oldInstruction , Instruction newInstruction ) {
501+ int index = instructionIndex .get (oldInstruction );
502+ instructions .set (index , newInstruction );
503+ instructionIndex .put (newInstruction , index );
504+ instructionIndex .remove (oldInstruction );
505+ }
506+
507+ @ Override
508+ public Iterator <Instruction > iterator () {
509+ return instructions .iterator ();
510+ }
511+ }
474512}
0 commit comments