All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.gradle.internal.kapt.incremental.ClasspathAnalyzer.kt Maven / Gradle / Ivy

There is a newer version: 2.0.20-RC
Show newest version
/*
 * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.gradle.internal.kapt.incremental

import org.gradle.api.artifacts.transform.ArtifactTransform
import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.ClassWriter
import java.io.*
import java.security.MessageDigest
import java.util.zip.ZipFile

const val CLASS_STRUCTURE_ARTIFACT_TYPE = "class-structure"
private const val MODULE_INFO = "module-info.class"

class StructureArtifactTransform : ArtifactTransform() {
    override fun transform(input: File): MutableList {
        try {
            val data = if (input.isDirectory) {
                visitDirectory(input)
            } else {
                visitJar(input)
            }

            val dataFile = outputDirectory.resolve("output.bin")
            data.saveTo(dataFile)

            return mutableListOf(dataFile)
        } catch (e: Throwable) {
            throw e
        }
    }
}

private fun visitDirectory(directory: File): ClasspathEntryData {
    val entryData = ClasspathEntryData()

    directory.walk().filter {
        it.extension == "class"
                && !it.relativeTo(directory).toString().toLowerCase().startsWith("meta-inf")
                && it.name != MODULE_INFO
    }.forEach {
        val internalName = it.relativeTo(directory).invariantSeparatorsPath.dropLast(".class".length)
        BufferedInputStream(it.inputStream()).use { inputStream ->
            analyzeInputStream(inputStream, internalName, entryData)
        }
    }

    return entryData
}

private fun visitJar(jar: File): ClasspathEntryData {
    val entryData = ClasspathEntryData()

    ZipFile(jar).use { zipFile ->
        val entries = zipFile.entries()
        while (entries.hasMoreElements()) {
            val entry = entries.nextElement()

            if (entry.name.endsWith("class")
                && !entry.name.toLowerCase().startsWith("meta-inf")
                && entry.name != MODULE_INFO
            ) {
                BufferedInputStream(zipFile.getInputStream(entry)).use { inputStream ->
                    analyzeInputStream(inputStream, entry.name.dropLast(".class".length), entryData)
                }
            }
        }
    }

    return entryData
}

private fun analyzeInputStream(input: InputStream, internalName: String, entryData: ClasspathEntryData) {
    val abiExtractor = ClassAbiExtractor(ClassWriter(0))
    val typeDependenciesExtractor = ClassTypeExtractorVisitor(abiExtractor)
    ClassReader(input.readBytes()).accept(
        typeDependenciesExtractor,
        ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES
    )

    val bytes = abiExtractor.getBytes()
    val digest = MessageDigest.getInstance("MD5").digest(bytes)

    entryData.classAbiHash[internalName] = digest
    entryData.classDependencies[internalName] =
        ClassDependencies(typeDependenciesExtractor.getAbiTypes(), typeDependenciesExtractor.getPrivateTypes())
}

class ClasspathEntryData : Serializable {

    object ClasspathEntrySerializer {
        fun loadFrom(file: File): ClasspathEntryData {
            ObjectInputStream(BufferedInputStream(file.inputStream())).use {
                return it.readObject() as ClasspathEntryData
            }
        }
    }

    @Transient
    var classAbiHash = mutableMapOf()
    @Transient
    var classDependencies = mutableMapOf()

    private fun writeObject(output: ObjectOutputStream) {
        val names = mutableMapOf()
        classAbiHash.keys.forEach { names[it] = names.size }
        classDependencies.values.forEach {
            it.abiTypes.forEach {
                if (!names.containsKey(it)) names[it] = names.size
            }
            it.privateTypes.forEach {
                if (!names.containsKey(it)) names[it] = names.size
            }
        }

        output.writeInt(names.size)
        names.forEach { key, value ->
            output.writeInt(value)
            output.writeUTF(key)
        }

        output.writeInt(classAbiHash.size)
        classAbiHash.forEach {
            output.writeInt(names[it.key]!!)
            output.writeInt(it.value.size)
            output.write(it.value)
        }

        output.writeInt(classDependencies.size)
        classDependencies.forEach {
            output.writeInt(names[it.key]!!)

            output.writeInt(it.value.abiTypes.size)
            it.value.abiTypes.forEach {
                output.writeInt(names[it]!!)
            }

            output.writeInt(it.value.privateTypes.size)
            it.value.privateTypes.forEach {
                output.writeInt(names[it]!!)
            }
        }
    }

    @Suppress("UNCHECKED_CAST")
    private fun readObject(input: ObjectInputStream) {
        val namesSize = input.readInt()
        val names = HashMap(namesSize)
        repeat(namesSize) {
            val classId = input.readInt()
            val classInternalName = input.readUTF()
            names[classId] = classInternalName
        }

        val abiHashesSize = input.readInt()
        classAbiHash = HashMap(abiHashesSize)
        repeat(abiHashesSize) {
            val internalName = names[input.readInt()]!!
            val byteArraySize = input.readInt()
            val hash = ByteArray(byteArraySize)
            repeat(byteArraySize) {
                hash[it] = input.readByte()
            }
            classAbiHash[internalName] = hash
        }

        val dependenciesSize = input.readInt()
        classDependencies = HashMap(dependenciesSize)

        repeat(dependenciesSize) {
            val internalName = names[input.readInt()]!!

            val abiTypesSize = input.readInt()
            val abiTypeNames = HashSet(abiTypesSize)
            repeat(abiTypesSize) {
                abiTypeNames.add(names[input.readInt()]!!)
            }

            val privateTypesSize = input.readInt()
            val privateTypeNames = HashSet(privateTypesSize)
            repeat(privateTypesSize) {
                privateTypeNames.add(names[input.readInt()]!!)
            }

            classDependencies[internalName] = ClassDependencies(abiTypeNames, privateTypeNames)
        }
    }

    fun saveTo(file: File) {
        ObjectOutputStream(BufferedOutputStream(file.outputStream())).use {
            it.writeObject(this)
        }
    }
}

class ClassDependencies(val abiTypes: Collection, val privateTypes: Collection)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy