override fun onMethodCall()

in src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt [1703:1822]


    override fun onMethodCall(
        threadDescriptor: ThreadDescriptor,
        codeLocation: Int,
        methodId: Int,
        receiver: Any?,
        params: Array<Any?>,
        interceptor: ResultInterceptor?,
    ): Unit = threadDescriptor.runInsideIgnoredSection {
        val methodDescriptor = context.getMethodDescriptor(methodId)
        // check if the called method is an atomics API method
        // (e.g., Atomic classes, AFU, VarHandle memory access API, etc.)
        val atomicMethodDescriptor = getAtomicMethodDescriptor(receiver, methodDescriptor.methodName)
        // process method effect on the static memory snapshot
        processMethodEffectOnStaticSnapshot(receiver, params, atomicMethodDescriptor)
        val threadId = threadScheduler.getCurrentThreadId()
        // re-throw abort error if the thread was aborted
        if (threadScheduler.isAborted(threadId)) {
            threadScheduler.abortCurrentThread()
        }

        // obtain deterministic method descriptor if required
        val methodCallInfo = MethodCallInfo(
            ownerType = Types.ObjectType(methodDescriptor.className),
            methodSignature = methodDescriptor.methodSignature,
            codeLocation = codeLocation,
            methodId = methodId,
        )

        val deterministicMethodDescriptor = getDeterministicMethodDescriptorOrNull(receiver, params, methodCallInfo)
        if (deterministicMethodDescriptor != null) {
            deterministicMethodDescriptor.processDeterministicMethodCall(receiver, params, methodCallInfo, interceptor)
        } else {
            // we are at normal deterministic method => no need to enforce the deterministic result => do nothing
        }

        // get method's analysis section type
        val methodSection = methodAnalysisSectionType(
            receiver,
            methodDescriptor.className,
            methodDescriptor.methodName,
            atomicMethodDescriptor,
            deterministicMethodDescriptor,
        )
        // in case if a static method is called, ensure its class is instrumented
        if (receiver == null && methodSection < AnalysisSectionType.ATOMIC) {
            LincheckJavaAgent.ensureClassHierarchyIsTransformed(methodDescriptor.className)
        }
        // in the case of atomics API setter method call, notify the object tracker about a new link between objects
        if (atomicMethodDescriptor != null && atomicMethodDescriptor.kind.isSetter) {
            objectTracker.registerObjectLink(
                fromObject = atomicMethodDescriptor.getAccessedObject(receiver, params),
                toObject = atomicMethodDescriptor.getSetValue(receiver, params)
            )
        }
        // Should this method call be ignored?
        if (methodSection == AnalysisSectionType.IGNORED) {
            enterAnalysisSection(threadId, methodSection)
            return
        }
        // in case of an atomic method, we create a switch point before the method call;
        // note that in case we resume atomic method there is no need to create the switch point,
        // since there is already a switch point between the suspension point and resumption
        if (methodSection == AnalysisSectionType.ATOMIC &&
            // do not create a trace point on resumption
            !isResumptionMethodCall(
                threadId, methodDescriptor.className,
                methodDescriptor.methodName, params, atomicMethodDescriptor
            )
        ) {
            // create a switch point
            newSwitchPoint(threadId, codeLocation)
            // check for livelock and create the method call trace point
            traceCollector?.checkActiveLockDetected()
            // create a trace point
            val eventId = getNextEventId()
            val tracePoint = addBeforeMethodCallTracePoint(
                eventId = eventId,
                threadId = threadId,
                owner = receiver,
                codeLocation = codeLocation,
                methodId = methodId,
                className = methodDescriptor.className,
                methodName = methodDescriptor.methodName,
                methodParams = params,
                atomicMethodDescriptor = atomicMethodDescriptor,
                callType = MethodCallTracePoint.CallType.NORMAL,
            )
            // add trace point to the trace
            traceCollector?.addTracePointInternal(tracePoint)
            // notify loop detector
            loopDetector.beforeAtomicMethodCall(codeLocation, params)
        } else {
            // handle non-atomic methods

            // check for livelock and create the method call trace point
            traceCollector?.checkActiveLockDetected()
            // create a trace point
            val eventId = getNextEventId()
            val tracePoint = addBeforeMethodCallTracePoint(
                eventId = eventId,
                threadId = threadId,
                owner = receiver,
                codeLocation = codeLocation,
                methodId = methodId,
                className = methodDescriptor.className,
                methodName = methodDescriptor.methodName,
                methodParams = params,
                atomicMethodDescriptor = atomicMethodDescriptor,
                callType = MethodCallTracePoint.CallType.NORMAL,
            )
            // add trace point to the trace
            traceCollector?.addTracePointInternal(tracePoint)
            // notify loop detector about the method call
            if (methodSection < AnalysisSectionType.ATOMIC) {
                loopDetector.beforeMethodCall(codeLocation, params)
            }
        }
        // if the method has certain guarantees, enter the corresponding section
        enterAnalysisSection(threadId, methodSection)
    }