This PR also changes the way classes bytecode is restore to the initially loaded into Lincheck agent. Instead of redefineClasses(...) now retransformClasses(...) is used in order to avoid double-instrumentation by other attached agents (e.g. intellij-coverage agent was throwing ClassFormatError because of this problem).
Description
Relates to #254.
This PR adds a bytecode filter class, which "hides" bytecode generated by intellij-coverage agent (e.g. when using kover gradle plugin) from transformation that exist in Lincheck.
The state of automata is reset when starting to instrument a new method
HITS_INIT and HITS_INIT_FIELD states occur, when the hits array (which stores coverage data) is loaded onto stack. The difference between state is the bytecode that they target: the 1st one occurs when hits was loaded via LDC instruction, the 2nd when GETSTATIC instruction was used.
The way intellij coverage loads hits using LDC is as follows:
Here GETSTATIC is called twice, that is why on the diagram above there is a double-sided arrow between INITIAL and LOADED_ON_STACK_STATIC states (first time hits is loaded on stack to check if it is null, then it is discarded, and lastly it is put on stack once again).
HITS_IN_LOCAL occurs when the hits array is already saved to local variables of the method and the automata knows its index.
When the ALOAD #n instruction occurs automata goes into HITS_BEFORE_ASSIGN state, then it searches for bytecode of hits[index] = 1 or/and hits[index] += 1. When the assignment completed the state goes back to HITS_IN_LOCAL.
When the method is fully transformed, the automata is put to INITIAL state again.
Notice
This PR also changes the way classes bytecode is restore to the initially loaded into Lincheck agent. Instead of
redefineClasses(...)
nowretransformClasses(...)
is used in order to avoid double-instrumentation by other attached agents (e.g. intellij-coverage agent was throwingClassFormatError
because of this problem).Description
Relates to #254.
This PR adds a bytecode filter class, which "hides" bytecode generated by intellij-coverage agent (e.g. when using kover gradle plugin) from transformation that exist in Lincheck.
hits[index] = 1
)hits[index] += 1
)The bytecode tracking is implemented as state machine with 5 states (
+
symbol designates the direction of an arrow):Short overview:
HITS_INIT
andHITS_INIT_FIELD
states occur, when thehits
array (which stores coverage data) is loaded onto stack. The difference between state is the bytecode that they target: the 1st one occurs whenhits
was loaded viaLDC
instruction, the 2nd whenGETSTATIC
instruction was used.hits
usingLDC
is as follows:and using the
GETSTATIC
:Here
GETSTATIC
is called twice, that is why on the diagram above there is a double-sided arrow betweenINITIAL
andLOADED_ON_STACK_STATIC
states (first timehits
is loaded on stack to check if it is null, then it is discarded, and lastly it is put on stack once again).HITS_IN_LOCAL
occurs when thehits
array is already saved to local variables of the method and the automata knows its index.ALOAD #n
instruction occurs automata goes intoHITS_BEFORE_ASSIGN
state, then it searches for bytecode ofhits[index] = 1
or/andhits[index] += 1
. When the assignment completed the state goes back toHITS_IN_LOCAL
.INITIAL
state again.