in src/jvm/main/org/jetbrains/kotlinx/lincheck/runner/TestThreadExecutionGenerator.java [130:310]
private static void generateRun(ClassVisitor cv, Type testType, int iThread, List<Actor> actors,
List<Object> objArgs, List<Continuation> completions,
boolean scenarioContainsSuspendableActors)
{
int access = ACC_PUBLIC;
Method m = new Method("run", VOID_TYPE, NO_ARGS);
GeneratorAdapter mv = new GeneratorAdapter(access, m,
// Try-catch blocks sorting is required
new TryCatchBlockSorter(cv.visitMethod(access, m.getName(), m.getDescriptor(), null, null),
access, m.getName(), m.getDescriptor(), null, null)
);
mv.visitCode();
// Load `results`
int resLocal = mv.newLocal(RESULT_ARRAY_TYPE);
mv.loadThis();
mv.getField(TEST_THREAD_EXECUTION_TYPE, "results", RESULT_ARRAY_TYPE);
mv.storeLocal(resLocal);
// Call runner's onThreadStart(iThread) method
mv.loadThis();
mv.getField(TEST_THREAD_EXECUTION_TYPE, "runner", RUNNER_TYPE);
mv.push(iThread);
mv.invokeVirtual(RUNNER_TYPE, RUNNER_ON_THREAD_START_METHOD);
// wrap actor's running loop in try-finally
Label actorsRunningLoopBlockStart = mv.newLabel();
Label actorsRunningLoopBlockEnd = mv.newLabel();
Label actorsRunningLoopBlockFinally = mv.newLabel();
mv.visitTryCatchBlock(actorsRunningLoopBlockStart, actorsRunningLoopBlockEnd, actorsRunningLoopBlockFinally, null);
mv.visitLabel(actorsRunningLoopBlockStart);
// Number of current operation (starts with 0)
int iLocal = mv.newLocal(INT_TYPE);
mv.push(0);
mv.storeLocal(iLocal);
// Invoke actors
for (int i = 0; i < actors.size(); i++) {
readClocksIfNeeded(i, mv);
Label returnNoResult = mv.newLabel();
if (scenarioContainsSuspendableActors) {
// check whether all threads are completed or suspended
mv.loadThis();
mv.getField(TEST_THREAD_EXECUTION_TYPE, "runner", RUNNER_TYPE);
mv.invokeVirtual(RUNNER_TYPE, RUNNER_IS_PARALLEL_EXECUTION_COMPLETED_METHOD);
mv.push(true);
mv.ifCmp(BOOLEAN_TYPE, GeneratorAdapter.EQ, returnNoResult);
}
Actor actor = actors.get(i);
// Start of try-catch block for exceptions which this actor should handle
Label exceptionHandler = mv.newLabel();
Label actorCatchBlockStart = mv.newLabel();
Label actorCatchBlockEnd = mv.newLabel();
mv.visitTryCatchBlock(actorCatchBlockStart, actorCatchBlockEnd, exceptionHandler, THROWABLE_TYPE.getInternalName());
mv.visitLabel(actorCatchBlockStart);
// onActorStart call
mv.loadThis();
mv.getField(TEST_THREAD_EXECUTION_TYPE, "runner", RUNNER_TYPE);
mv.push(iThread);
mv.invokeVirtual(RUNNER_TYPE, RUNNER_ON_ACTOR_START);
// Load result array and index to store the current result
mv.loadLocal(resLocal);
mv.push(i);
if (scenarioContainsSuspendableActors) {
// push the instance of ExecutionScenarioRunner on stack to call it's processInvocationResult method
mv.loadThis();
mv.getField(TEST_THREAD_EXECUTION_TYPE, "runner", RUNNER_TYPE);
mv.checkCast(RUNNER_TYPE);
} else {
// Prepare to create non-processed value result of actor invocation (in case of no suspendable actors in scenario)
// Load type of result
if (actor.getMethod().getReturnType() != void.class) {
mv.newInstance(VALUE_RESULT_TYPE);
mv.visitInsn(DUP);
}
}
// Load test instance
mv.loadThis();
mv.getField(TEST_THREAD_EXECUTION_TYPE, "testInstance", OBJECT_TYPE);
mv.checkCast(testType);
// Load arguments for operation
loadArguments(mv, actor, objArgs, actor.isSuspendable() ? completions.get(i) : null);
// Invoke operation
Method actorMethod = Method.getMethod(actor.getMethod());
mv.invokeVirtual(testType, actorMethod);
mv.box(actorMethod.getReturnType()); // box if needed
if (scenarioContainsSuspendableActors) {
// process result of method invocation with
// `ExecutionScenarioRunner::processInvocationResult(result, iThread, i)`
mv.push(iThread);
mv.push(i);
mv.invokeVirtual(RUNNER_TYPE, RUNNER_PROCESS_INVOCATION_RESULT_METHOD);
if (actor.getMethod().getReturnType() == void.class) {
createVoidResult(actor, mv);
}
} else {
// Create result
if (actor.getMethod().getReturnType() == void.class) {
createVoidResult(actor, mv);
} else {
mv.invokeConstructor(VALUE_RESULT_TYPE, VALUE_RESULT_TYPE_CONSTRUCTOR);
}
}
// Store result to array
mv.arrayStore(RESULT_TYPE);
// End of try-catch block for handled exceptions
mv.visitLabel(actorCatchBlockEnd);
Label skipHandlers = mv.newLabel();
mv.goTo(skipHandlers);
// Exception handler
mv.visitLabel(exceptionHandler);
int eLocal = mv.newLocal(THROWABLE_TYPE);
mv.storeLocal(eLocal);
// push the runner on stack to call its method
mv.loadThis();
mv.getField(TEST_THREAD_EXECUTION_TYPE, "runner", RUNNER_TYPE);
// push iThread and exception on stack
mv.push(iThread);
mv.loadLocal(eLocal);
// Fail if this exception is an internal exception
mv.invokeVirtual(RUNNER_TYPE, RUNNER_ON_ACTOR_FAILURE_METHOD);
mv.loadLocal(eLocal);
if (scenarioContainsSuspendableActors) {
storeExceptionResultFromSuspendableThrowable(mv, resLocal, iLocal, iThread, i);
} else {
storeExceptionResultFromThrowable(mv, resLocal, iLocal);
}
// End of try-catch block for all other exceptions
mv.goTo(skipHandlers);
mv.visitLabel(skipHandlers);
// Invoke runner onActorFinish method
mv.loadThis();
mv.getField(TEST_THREAD_EXECUTION_TYPE, "runner", RUNNER_TYPE);
mv.push(iThread);
mv.invokeVirtual(RUNNER_TYPE, RUNNER_ON_ACTOR_FINISH);
// Increment the clock
mv.loadThis();
mv.invokeVirtual(TEST_THREAD_EXECUTION_TYPE, TEST_THREAD_EXECUTION_INC_CLOCK);
// Increment number of current operation
mv.iinc(iLocal, 1);
Label launchNextActor = mv.newLabel();
mv.visitJumpInsn(GOTO, launchNextActor);
// write NoResult if all threads were suspended or completed
mv.visitLabel(returnNoResult);
mv.loadLocal(resLocal);
mv.push(i);
// Load no result type to create new instance of NoResult
mv.visitFieldInsn(GETSTATIC, NO_RESULT_CLASS_NAME, INSTANCE, NO_RESULT_TYPE.getDescriptor());
mv.arrayStore(RESULT_TYPE);
mv.iinc(iLocal, 1);
mv.visitLabel(launchNextActor);
}
mv.visitInsn(ACONST_NULL); // push null exception value indicating normal method's termination
mv.goTo(actorsRunningLoopBlockFinally);
mv.visitLabel(actorsRunningLoopBlockEnd);
mv.visitLabel(actorsRunningLoopBlockFinally);
// Call runner's onThreadFinish(iThread) method
mv.loadThis();
mv.getField(TEST_THREAD_EXECUTION_TYPE, "runner", RUNNER_TYPE);
mv.push(iThread);
mv.invokeVirtual(RUNNER_TYPE, RUNNER_ON_THREAD_FINISH_METHOD);
// Check if an exception was thrown in the actors' running loop and re-throw it
Label methodReturnLabel = mv.newLabel();
mv.dup();
mv.ifNull(methodReturnLabel);
mv.throwException(); // re-throw exception
// Complete the method
mv.visitLabel(methodReturnLabel);
mv.pop(); // pop null exception value indicating normal method's termination
mv.visitInsn(RETURN);
mv.visitMaxs(3, 4);
mv.visitEnd();
}