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

Exclude from a report a part of bytecode that compiler generates for a try-with-resources #500

Merged
merged 27 commits into from
Apr 22, 2017

Conversation

Godin
Copy link
Member

@Godin Godin commented Mar 16, 2017

@marchof
Copy link
Member

marchof commented Mar 17, 2017

@Godin Count me deeply impressed!!! And I'm very sorry that I couldn't immediately jump into both PRs. So+Mo are travel dates, so I hope to study it in detail then.

@Godin
Copy link
Member Author

Godin commented Mar 17, 2017

@marchof no worry, will be cool to get all this on board, but no need to rush - let's do a proper careful review and testing. Slowly, but at least we are progressing on this big subject. And I personally start to see the light at the end of the tunnel, which wasn't the case few months ago.

FYI next week I'm also kind of traveling - will attend DevoxxUS, and hope to not be offline all the time.

@bjkail
Copy link
Contributor

bjkail commented Mar 17, 2017

@Godin It would be useful if the members of Pattern had high-level descriptions of the code being matched and which conditions cause the compiler to generate that pattern. It would be good to have a test of t-w-r with custom finally block (try (...) { ... } finally { nop() }) to be sure it doesn't cause problems for the analyses. It would also be good to have tests with nested t-w-r nested in the try, catch, and finally blocks (e.g., try (...) { ... } finally { try (...) { ... } catch (...) { ... } finally { ... } }, etc.).

@Godin
Copy link
Member Author

Godin commented Mar 17, 2017

@bjkail Hey, glad to see you here! Actually was about to ask if you're interested in this subject and if so maybe you could leave some valuable comments as before 😉 And here they are 😋

You are right on all points - let's cleanup and harden this PoC. Also wondering WDTY about approach in general? About it we'd better add explanatory comments also. As well as estimate impact on performance of analysis - can imagine some optimizations... but traded them on the correctness and attempt to keep relative simplicity of code for now.

Anyway thank you! we really appreciate your help 👍

@bjkail
Copy link
Contributor

bjkail commented Mar 17, 2017

@Godin The overall approach seems reasonable to me of skipping ignored instructions in MethodAnalyzer.visitEnd and matching bytecode sequences in TryWithResourcesMatcher.

Last time I thought about t-w-r, I was thinking to require the t-w-r close sequence be replicated at the end of the try block and each catch block (including Throwable) to ensure it was either written by a compiler or fully correctly hand-written. My motivation was to avoid filtering out a badly hand-written close sequence (e.g., at the end of try block only but not all catch blocks or missing Throwable block, etc.), but the algorithm to do the rigid matching seemed quite complex, especially for custom finally blocks and nested t-w-r (thus my suggested testcases). However, I am now thinking your approach is fine. There is some risk of false positive (matching a hand-written close sequence), but the risk is low since the close sequence is relatively complex, and we can leave detecting badly hand-written close sequences (like missing close sequence on some possible code path) to static analyzers like FindBugs.

@bjkail
Copy link
Contributor

bjkail commented Mar 17, 2017

As an anecdote, last time I investigated t-w-r was because we kept regressing coverage in classes that use t-w-r heavily because drops go unnoticed due to all the uncovered generated code. I tried forcing all the generate code paths to be executed (our code/tests made it easy to force all the conditions: null resource, exception with null resource, exception with non-null resource, close exception, exception and close exception), but I ultimately gave up when I discovered javac emits completely dead code:

  • if (#primaryExc) != null) is always false in the try block and is always true in the catch blocks.
  • if (Identifier != null) can be always false in the try block. JDK-7020499 improves that for Java 9, but I wish it also had "effectively non-null" analysis like the "effectively final" analysis that would elide the check.

So, I think this will be a great feature to have in JaCoCo :-).

@Godin Godin force-pushed the issue-500 branch 16 times, most recently from 50d98bc to 1adc2d1 Compare March 25, 2017 15:55
* initialized using <code>new</code></li>
* <li>synthetic method <code>$closeResource</code> containing
* <code>null</code> check of primaryExc and calls to methods
* <code>addSuppressed</code> and <code>close</code> is used when numbers of
Copy link
Member Author

Choose a reason for hiding this comment

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

typo - numbers instead of number

@Godin
Copy link
Member Author

Godin commented Apr 21, 2017

@marchof I scanned bigger subset of Maven Central - latest versions of some artifacts within group org. This was not easy, because not counting time that were spent to download - some jars with sources should be excluded as they are incomplete, contain more than jars with classes, contain templates for generation of sources, etc...

Nevertheless 9753 try-with-resources statements were filtered correctly from ≈20 Gb of JAR files with classes. And this time ECJ filter was triggered quite a good amount of times.

Case that was already presented in our tests and not filtered - when body of try-with-resources is empty

try (Resource r = new Resource()) {
}

was encountered in

  • org/apache/cassandra/cassandra-all/3.10
  • org/apache/cayenne/modeler/cayenne-modeler/4.0.M5
  • org/apache/reef/reef-runtime-hdinsight/0.15.0
  • org/apache/tomcat/embed/tomcat-embed-jasper/9.0.0.M19
  • org/apache/tomcat/tomcat-jasper/9.0.0.M19
  • org/apache/solr/solr-core/6.5.0
  • org/eclipse/scout/sdk/deps/org.eclipse.jdt.launching/3.8.100.v20160505-0636
  • org/integratedmodelling/klab-common/0.9.10
  • org/jboss/arquillian/container/arquillian-gae-common/1.0.0.Beta10
  • org/jboss/cdi/tck/cdi-tck-impl/2.0.0.CR1
  • org/neo4j/app/neo4j-server/3.2.0-alpha07
  • org/neo4j/neo4j-ha/3.2.0-alpha07
  • org/neo4j/neo4j-ogm/1.1.6
  • org/sonatype/nexus/bundles/org.sonatype.nexus.bundles.elasticsearch/3.2.1-01
  • org/sonarsource/sonarqube/sonar-process/6.3
  • org/simplericity/jettyconsole/jetty-console-core/1.60
  • org/vesalainen/comm/1.0.3
  • org/qi4j/core/org.qi4j.core.testsupport/2.1

Another funny case that was already presented in our tests and not filtered - when try-with-resources contains only throw statement

try (Resource r = new Resource()) {
  throw new Exception();
}

was encountered in

  • org/apache/nifi/nifi-framework-core/1.1.2
  • org/apache/reef/reef-common/0.15.0
  • org/codelibs/fess/fess-crawler/1.1.0
  • org/tentackle/tentackle-persistence/2.0.7
  • org/sonatype/nexus/bundles/org.sonatype.nexus.bundles.elasticsearch/3.2.1-01

Not explicitly presented in out tests, but a kind of variation of previous and not filtered - when there is no non-exceptional path from a body as for example

try (Resource r = new Resource()) {
  while (true) {
    read(r);
  }
} catch (EOFException e) {
}

was encountered in

  • org/apache/cassandra/cassandra-all/3.10
  • org/apache/fluo/fluo-accumulo/1.0.0-incubating
  • org/github/evenjn/knit/0.6.0
  • org/infinispan/infinispan-embedded/9.0.0.Final
  • org/jgroups/jgroups/4.0.1.Final
  • org/kohsuke/elb-dns/1.1

An interesting case where source code contains try-with-resources, but retrolambda removes call to Throwable.addSuppressed and so not filtered, was encountered in

  • org/ansj/ansj_seg/5.1.1

An interesting case where AspectJ adds try-with-resources into bytecode was encountered in

  • org/no-hope/aspectj/metrics-aspectj-el/1.7

All in all: I satisfied by results and would like to merge this PR. We can deal with cases of an empty body and absence of non-exceptional path in a separate PR. If you agree I'll squash and merge into master.

@marchof
Copy link
Member

marchof commented Apr 22, 2017

@Godin Awesome work, Evgeny! I'm absolutly curious about the exact test setup. And yes, we can add corner cases later.

I was already about to ask about the performance of the new, way more readable matching code. As there is no negative impact -> yes, please merge this!

@Godin Godin merged commit e93053e into master Apr 22, 2017
@Godin Godin deleted the issue-500 branch April 22, 2017 10:39
@romani
Copy link

romani commented Jun 29, 2017

@Godin , when do you plan to release this ?
Checkstyle project plan to give jacoco another try this summer, this feature could be a good stimulus to switch to jacoco for coverage.
jacoco is already 5month without release with bunch of good fixes.

@Godin
Copy link
Member Author

Godin commented Jun 29, 2017

@jacoco jacoco locked as resolved and limited conversation to collaborators Jan 11, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants