While trying to understand a behavior of TestNG I found my self disassembling the .class files. During that, I accidentally noticed that the official binary differs from the one built manually from source and from the source itself.
The reason I disassembled the binaries was just to prove that the binary really lacks local variable tables (and for fun), since I was unable inspecting local variables.
- Project: https://github.com/cbeust/testng.git
- Artifact Version / Tag: 6.11
- Source File:
TestNG.java
(Official Repository) - Class File:
TestNG.class
in testng-6.11.jar (repo.maven.apache.org) - Used JDK: OpenJDK 1.8.0 131
I built the source from the project repository at Tag 6.11. using Gradle.
In the first place I inspected both using javap -l -p TestNG.class
, writing the output into two separate files and diffing the resulting descriptions. So far so good.
Then, for fun, I disassembled both class files using these commands:
javap -c -p /tmp/TestNG.mvn.class > /tmp/TestNG.mvn.java
javap -c -p /tmp/TestNG.mine.class > /tmp/TestNG.mine.java
Then I diffed both and accidently found the private field m_executionListeners
to be missing.
--- /tmp/TestNG.mvn.java 2017-06-03 23:12:16.005211337 +0200
+++ /tmp/TestNG.mine.java 2017-06-03 23:12:08.245208191 +0200
@@ -92,6 +92,8 @@
protected long m_start;
+ private final java.util.Map<java.lang.Class<? extends org.testng.IExecutionListener>, org.testng.IExecutionListener> m_executionListeners;
+
(No, its declaration did not just move around, it's gone.)
So I thought it could be some kind of compiler optimization. But it is actually referenced and accessed two times. This is one access:
public void addListener(ITestNGListener listener) {
if (listener == null) {
return;
}
if (listener instanceof ISuiteListener) {
ISuiteListener suite = (ISuiteListener) listener;
maybeAddListener(m_suiteListeners, suite.getClass(), suite);
}
// ...
if (listener instanceof IExecutionListener) {
IExecutionListener execution = (IExecutionListener) listener;
maybeAddListener(m_executionListeners, execution.getClass(), execution);
}
// ...
}
All other fields that run through the instanceof
check, type cast and maybeAddListener()
call are still present.
Am I right, with my conclusion, that the binary has not been built from the (supposed-to-be) related source? Or what it going on here?