in jvm-agent/src/main/org/jetbrains/lincheck/jvm/agent/LincheckClassFileTransformer.kt [80:142]
fun transformImpl(
loader: ClassLoader?,
internalClassName: String,
classBytes: ByteArray
): ByteArray = transformedClassesCache.computeIfAbsent(internalClassName.toCanonicalClassName()) {
Logger.debug { "Transforming $internalClassName" }
val reader = ClassReader(classBytes)
// the following code is required for local variables access tracking
val classNode = ClassNode()
reader.accept(classNode, ClassReader.EXPAND_FRAMES)
val (includeClasses, excludeClasses) = if (instrumentationMode == TRACE_RECORDING) {
TraceAgentParameters.getIncludePatterns() to TraceAgentParameters.getExcludePatterns()
} else {
emptyList<String>() to emptyList<String>()
}
val profile = createTransformationProfile(
instrumentationMode,
includeClasses = includeClasses,
excludeClasses = excludeClasses,
)
// Don't use class/method visitors on classNode to collect labels, as
// MethodNode reset all labels on a re-visit (WHY?!).
// Only one visit is possible to have labels stable.
// Visiting components like `MethodNode.instructions` is safe.
val (lineRanges, linesToMethodNames) = getMethodsLineRanges(classNode)
val classInfo = ClassInformation(
smap = readClassSMAP(classNode, reader),
locals = getMethodsLocalVariables(classNode, profile),
labels = getMethodsLabels(classNode),
methodsToLineRanges = lineRanges,
linesToMethodNames = linesToMethodNames,
basicCfgs = computeControlFlowGraphs(classNode, profile),
)
val writer = SafeClassWriter(reader, loader, ClassWriter.COMPUTE_FRAMES)
val visitor = LincheckClassVisitor(writer, classInfo, instrumentationMode, profile, statsTracker, LincheckJavaAgent.context)
try {
val timeNano = measureTimeNano {
classNode.accept(visitor)
}
writer.toByteArray().also { transformedBytes ->
if (dumpTransformedSources) {
dumpClassBytecode(classNode.name, transformedBytes)
}
statsTracker?.saveStatistics(
originalClassNode = classNode,
originalClassBytes = classBytes,
transformedClassBytes = transformedBytes,
transformationTimeNanos = timeNano,
)
}
} catch (e: Throwable) {
Logger.error { "Unable to transform $internalClassName" }
Logger.error(e)
classBytes
}
}