fun transformImpl()

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