private static void generateRun()

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();
    }