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