I found another problem with the way I determined if a program had ended: Sometimes a program was deemed dead even though it was still alive. This was because I sent the “thread started” sync point just before Thread.start()
was exited, but after calling Thread.start0()
, the native method that actually starts the thread. That made it possible for the thread to start executing before the recorder was aware of it being alive. If another thread died before the “thread start” sync point reached the recorder, the count count erroneously drop to zero. I fixed that by sending the “thread start” sync point just before the call to Thread.start0()
.
To do that, I had to refactor the AInsertBeforeReturnStrategy
again. Now it wasn’t inserting before a return all the time, insertion was governed by another predicate — now there are three predicates, one each to decide if it’s the right class, right method, and right opcode.
For the opcode predicate, I needed more than one parameter, though, just the InstructionList
wasn’t enough, I needed at least the ClassFile
with the constant pool as well. The good old unary ILambda
wasn’t going to do it anymore.
Unfortunately, writing a generic N-ary lambda isn’t all that easy. Now I added different versions: ILambda<R,P>
is still a unary lambda, ILambda.Binary<R,P,Q>
is a binary lambda, ILambda.Ternary<R,P,Q,S>
is a ternary lambda, and ILambda.Nary<R,P>
is an N-ary lambda that uses variable argument list and that therefore is restricted to having parameters of the same type.
Writing these lambdas was fun, the generics just looked pretty, so I wrote decorators for the lambdas that turn a binary lambda to a unary lambda by binding a constant to one of its parameters (ILambda.Binary.Bind1st<R,P,Q>
is a unary lambda that binds a constant value to the first parameter of a binary lambda). Ternary lambdas can be turned into binary or unary lambdas that way (ILambda.Ternary.Bind3rd<R,P,Q,S>
is a binary lambda that binds a constant to the third parameter of a ternary lambda; ILambda.Ternary.Bind1st3rd<R,P,Q,S>
is a unary lambda binding constants to the first and third parameter of a ternary lambda). There are also adaptors to turn N-ary lambdas into unary, binary or ternary lambdas, and vice versa.
For N-ary lambdas, ILambda.Nary.Bind<R,P>
is an (N-1)-ary lambda that binds a constant to an index-specified parameter of an N-ary lambda. To avoid thick wrapping, ILambda.Nary.BindK<R,P>
is an (N-K)-ary lambda that binds constants to K index-specified parameters of an N-ary lambda.
For predicates and some operations dealing with Comparables
, I have already created lambdas: there are different flavors of And
, Or
, Xor
, Less
, Max
, and so on…
All of that was just an enjoyable detour, but it allowed me to refactor the AInsertBeforeReturnStrategy
into a very general AInsertBeforeOpcodeStrategy
very nicely.