Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add filter for bytecode that ECJ generates for String in switch #735

Merged
merged 11 commits into from
Aug 17, 2018

Conversation

Godin
Copy link
Member

@Godin Godin commented Aug 15, 2018

No description provided.

@Godin Godin added this to the 0.8.2 milestone Aug 15, 2018
@Godin Godin self-assigned this Aug 15, 2018
@Godin Godin requested a review from marchof August 15, 2018 01:53
@Godin Godin added this to Review in Current work items via automation Aug 15, 2018
@Godin Godin force-pushed the ecj_switch_string branch 2 times, most recently from a6a2d7c to 84816ea Compare August 15, 2018 01:59
@@ -134,4 +136,8 @@ public void merge(final AbstractInsnNode i1, final AbstractInsnNode i2) {
fail();
}

public void replace(AbstractInsnNode i, List<AbstractInsnNode> branches) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be removed by/after #734

@Godin Godin added this to To Do in Filtering via automation Aug 15, 2018
@Godin Godin moved this from To Do to In Progress in Filtering Aug 15, 2018
@@ -371,6 +378,22 @@ public void visitEnd() {
nodeToInstruction.get(r).merge(i);
}
}

// Replace:
for (final Map.Entry<AbstractInsnNode, List<AbstractInsnNode>> r : replacements
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this can be done during counting without need to modify instructions.

@@ -41,4 +43,6 @@
*/
void merge(AbstractInsnNode i1, AbstractInsnNode i2);

void replace(AbstractInsnNode i, List<AbstractInsnNode> branches);
Copy link
Member

@marchof marchof Aug 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder what exactly this method does... can you please add some JavaDoc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marchof I was about to ask your advice about how to document it based on what its implementation does 😉

Something like

	/**
	 * Marks instruction that should be considered as having branches into the
	 * given instructions during computation of coverage.
	 * 
	 * @param i
	 *            instruction to be replaced
	 * @param branches
	 *            targets of branches
	 */

?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so it does actually not replace the instruction but its branches. In this case I would rename the method and parameters to

replaceBranches(origin, targets)

Also maybe a second sentence can clarify the behavior: "The original target branches of the origin instruction are ignored."

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As like before this change term "branch" is fragile - we consider that each instruction has at least one branch and will be counted as covered when at least one branch is covered:

final int total = i.getBranches();
final int covered = i.getCoveredBranches();
final ICounter instrCounter = covered == 0 ? CounterImpl.COUNTER_1_0
: CounterImpl.COUNTER_0_1;
final ICounter branchCounter = total > 1
? CounterImpl.getInstance(total - covered, covered)
: CounterImpl.COUNTER_0_0;
coverage.increment(instrCounter, branchCounter, i.getLine());

So in some sense this replaces instruction and not just its branches:

final int total;
final int covered;
final List<AbstractInsnNode> r = replacements.get(i.getNode());
if (r != null) {
int cb = 0;
for (AbstractInsnNode b : r) {
if (nodeToInstruction.get(b).getCoveredBranches() > 0) {
cb++;
}
}
total = r.size();
covered = cb;
} else {
total = i.getBranches();
covered = i.getCoveredBranches();
}
final ICounter instrCounter = covered == 0 ? CounterImpl.COUNTER_1_0
: CounterImpl.COUNTER_0_1;
final ICounter branchCounter = total > 1
? CounterImpl.getInstance(total - covered, covered)
: CounterImpl.COUNTER_0_0;
coverage.increment(instrCounter, branchCounter, i.getLine());

and while we don't have use-case and don't need this, even

output.replace(original, Collections.singletonList(replacement))

works.

Thus not sure about renaming of method.
Renaming of second parameter into targets sounds good.
And for first one feel that original sounds better than origin.


I like the idea to add second statement 👍 with slight rewording

The original target branches of the original instruction will be ignored.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, at least in the Analyzer I used the term "branch" as a synonym for a edge in the control flow graph. But with the JavaDoc we can leave the method as is.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marchof I added

  • Javadoc to IFilterOutput
  • tests to MethodAnalyzerTest
  • changelog entry

m.visitLabel(h2);

m.visitVarInsn(Opcodes.ALOAD, 2);
m.visitLdcInsn("\0a");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy-paste mistake - here and below should be "b" instead of "\0a".

* @param targets
* new targets of branches
*/
void replace(AbstractInsnNode original, List<AbstractInsnNode> targets);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Godin Sorry for coming back to this API again. I still think the API is confusing and can be improved:

  1. "instruction to be replaced": The instruction is not replaced, only the CFG edges to its successors is replaced by new edges.
  2. The targets have no order and must be unique. Should be a Set.

Here is my proposal:

    /**
     * Replaces the outgoing branches of a given instruction with new target
     * target instructions.
     *
     * @param source
     *            instruction which branches should be replaced
     * @param newTargets
     *            new targets of the branches
     */
    void replaceBranches(AbstractInsnNode source,
    		Set<AbstractInsnNode> newTargets);

I know I introduced the term "branch" again. But this is what we conistently use in the JaCoCo code base and from my point of view it simplifies the description so that the second phrase is not needed at all any more.

WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The targets have no order and must be unique. Should be a Set.

Actually there is order, which is IMO well defined and very important property of/in MethodAnalyzer, otherwise how merge works? 😉 In particular default of switch has number 0:

int branch = 0;
visitSwitchTarget(dflt, branch);

And so maybe List will be less confusing and more consistent even if doesn't play role in this API ?


The instruction is not replaced, only the CFG edges to its successors is replaced by new edges.

As we discussed earlier and as in implementation - we don't modify CFG, this affects only afterwards computation.

I know I introduced the term "branch" again.

No problem with term "branch" - it is already in Javadoc.

Right now I realized that with or without renaming of method on replaceBranches from this interface alone not clear that replacement of branches changes coverage of instruction and not just branch coverage.


    /**
     * Replaces the outgoing branches of a given instruction with new target

Other methods use term mark (consistency?) and state that they affect stage of computations, i.e. not the original CFG and not the propagation of probes, what again as was discussed earlier is quite important in this case. So not sure that this is cleaner/better than current

	/**
	 * Marks instruction that should be considered as having branches into the
	 * given instructions during computation of coverage.

Can imagine combination of both:

 	/**
	 * Marks instruction whose outgoing branches should be replaced during
	 * computation of coverage.
 	 * 
 	 * @param source
	 *            instruction which branches should be replaced
 	 * @param newTargets
	 *            new targets of branches
 	 */
	void replaceBranches(AbstractInsnNode source,

In either case to me seems that important things are missing in this documentation:

  • merge is performed before ignore and replace
  • ignore doesn't affect merge and targets in replace, but affects source in replace
  • targets in replace are affected by merges
  • replace overrides merges of source

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marchof as we decided by phone:

  • used my proposal of "combined version" for JavaDoc
  • method and parameters were renamed
  • List was replaced by Set

We'll think later about other improvements of documentation.

Copy link
Member

@marchof marchof left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this impressive work!

Just waiting for the green build now.

@marchof marchof merged commit 4741fb6 into jacoco:master Aug 17, 2018
Filtering automation moved this from In Progress to Done Aug 17, 2018
Current work items automation moved this from Review to Done Aug 17, 2018
@Godin Godin deleted the ecj_switch_string branch August 17, 2018 19:37
@jacoco jacoco locked as resolved and limited conversation to collaborators Oct 8, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
Filtering
  
Done
Development

Successfully merging this pull request may close these issues.

None yet

2 participants