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
}