override fun visitMethod()

in jvm-agent/src/main/org/jetbrains/lincheck/jvm/agent/LincheckClassVisitor.kt [67:218]


    override fun visitMethod(
        access: Int,
        methodName: String,
        desc: String,
        signature: String?,
        exceptions: Array<String>?
    ): MethodVisitor {
        var mv = super.visitMethod(access, methodName, desc, signature, exceptions)

        val isNative = (access and ACC_NATIVE != 0)
        val isSynchronized = (access and ACC_SYNCHRONIZED != 0)

        val methodInfo = classInformation.methodInformation(methodName, desc)
        val config = profile.getMethodConfiguration(className.toCanonicalClassName(), methodName, desc)

        if (isNative) {
            Logger.debug { "Skipping transformation of the native method $className.$methodName" }
            return mv
        } else if (config == TransformationConfiguration.UNTRACKED) {
            Logger.debug { "Skipping transformation of the untracked method $className.$methodName" }
        } else {
            Logger.debug { "Transforming method $className.$methodName" }
        }

        if (statsTracker != null) {
            mv = statsTracker.createStatisticsCollectingVisitor(className, methodName, desc, mv)
        }

        // in stress mode there are no complex transformations, so we do not need to handle try-catch blocks
        if (instrumentationMode != STRESS) {
            mv = JSRInlinerAdapter(mv, access, methodName, desc, signature, exceptions)
            mv = TryCatchBlockSorter(mv, access, methodName, desc, signature, exceptions)
        }

        val initialVisitor = mv
        val adapter = GeneratorAdapter(mv, access, methodName, desc)
        mv = adapter

        val chain = TransformerChain(
            config = config,
            adapter = adapter,
            initialMethodVisitor = mv,
        )

        // ======== Ignored Sections ========
        chain.addTransformer { adapter, mv ->
            IgnoredSectionWrapperTransformer(fileName, className, methodName, desc, access, methodInfo, context, adapter, mv)
        }

        // ======== Coroutines ========
        chain.addTransformer { adapter, mv ->
            CoroutineCancellabilitySupportTransformer(fileName, className, methodName, desc, access, methodInfo, context, adapter, mv)
        }
        chain.addTransformer { adapter, mv ->
            CoroutineDelaySupportTransformer(fileName, className, methodName, desc, access, methodInfo, context, adapter, mv)
        }

        // ======== Threads ========
        chain.addTransformer { adapter, mv ->
            ThreadRunTransformer(fileName, className, methodName, methodInfo, context, desc, access, adapter, mv, config)
        }
        chain.addTransformer { adapter, mv ->
            ThreadStartJoinTransformer(fileName, className, methodName, methodInfo, context, desc, access, adapter, mv, config)
        }

        // ======== Method Calls ========
        chain.addTransformer { adapter, mv ->
            MethodCallTransformer(fileName, className, methodName, desc, access, methodInfo, context, adapter, mv, config)
        }

        // ======== Object Creation ========
        chain.addTransformer { adapter, mv ->
            ObjectCreationTransformer(fileName, className, methodName, desc, access, methodInfo, context, adapter, mv)
        }

        // ======== Invokedynamic ========
        chain.addTransformer { adapter, mv ->
            DeterministicInvokeDynamicTransformer(fileName, className, methodName, desc, access, methodInfo, context, classVersion, adapter, mv)
        }

        // ======== Hash codes ========
        chain.addTransformer { adapter, mv ->
            ConstantHashCodeTransformer(fileName, className, methodName, desc, access, methodInfo, context, adapter, mv)
        }

        // ======== Synchronization primitives ========
        if (isSynchronized) {
            chain.addTransformer { adapter, mv ->
                SynchronizedMethodTransformer(fileName, className, methodName, desc, access, methodInfo, context, classVersion, adapter, mv)
            }
        }
        chain.addTransformer { adapter, mv ->
            MonitorTransformer(fileName, className, methodName, desc, access, methodInfo, context, adapter, mv)
        }
        chain.addTransformer { adapter, mv ->
            WaitNotifyTransformer(fileName, className, methodName, desc, access, methodInfo, context, adapter, mv)
        }
        chain.addTransformer { adapter, mv ->
            ParkingTransformer(fileName, className, methodName, desc, access, methodInfo, context, adapter, mv)
        }

        // ======== Field, Array, and Local Variables accesses ========
        chain.addTransformer { adapter, mv ->
            applySharedMemoryAccessTransformer(methodName, desc, access, methodInfo, config, adapter, mv)
        }
        chain.addTransformer { adapter, mv ->
            LocalVariablesAccessTransformer(fileName, className, methodName, desc, access, methodInfo, context, adapter, mv, config)
        }

        // ======== Inline Method Calls ========
        chain.addTransformer { adapter, mv ->
            InlineMethodCallTransformer(fileName, className, methodName, desc, access, methodInfo, context, adapter, mv)
        }

        // ======== Loops ========
        // TODO: we put loop transformer at the beginning of the chain,
        //   because it relies on original bytecode instruction numeration
        //   (it should match the instruction numeration in CFG).
        //   The placement of this transformer in the chain should not actually matter,
        //   because intermediate transformers should not affect downstream transformers in the chain,
        //   as they should normally supply original bytecode to them
        //   (and otherwise use singe `adapter` placed at the end of the chain to inject new bytecode).
        //   But apparently this assumption is currently violated,
        //   and most likely we have some bug in one of the transformers.
        chain.addTransformer { adapter, mv ->
            LoopTransformer(fileName, className, methodName, desc, access, methodInfo, context, adapter, mv)
        }

        // ======== Analyzers ========
        chain.addTypeAnalyzerAdapter(access, className, methodName, desc)
        chain.addOwnerNameAnalyzerAdapter(access, className, methodName, desc, methodInfo, context)

        mv = chain.methodVisitors.last()

        // This tacker must be before all transformers that use MethodVariables to track variable regions
        mv = LabelsTracker(mv, methodInfo)

        // Must appear in code after `SharedMemoryAccessTransformer` (to be able to skip this transformer).
        // It can appear earlier in code than `IntrinsicCandidateMethodFilter` because if kover instruments intrinsic methods
        // (which cannot disallow) then we don't need to hide coverage instrumentation from lincheck,
        // because lincheck will not see intrinsic method bodies at all.
        if (instrumentationMode == MODEL_CHECKING || instrumentationMode == TRACE_DEBUGGING) {
            mv = CoverageBytecodeFilter(initialVisitor, mv)
        }

        // Must appear last in the code, to completely hide intrinsic candidate methods from all transformers
        if (instrumentationMode == MODEL_CHECKING || instrumentationMode == TRACE_DEBUGGING) {
            mv = IntrinsicCandidateMethodFilter(className, methodName, desc, initialVisitor, mv, context)
        }

        return mv
    }