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

com.flyjingfish.android_aop_plugin.utils.WovenInfoUtils.kt Maven / Gradle / Ivy

Go to download

Lightweight Aop for Android platform, you deserve it, action is worse than your heartbeat

There is a newer version: 2.2.8
Show newest version
package com.flyjingfish.android_aop_plugin.utils

import com.flyjingfish.android_aop_plugin.beans.AopCollectClass
import com.flyjingfish.android_aop_plugin.beans.AopCollectCut
import com.flyjingfish.android_aop_plugin.beans.AopMatchCut
import com.flyjingfish.android_aop_plugin.beans.AopMethodCut
import com.flyjingfish.android_aop_plugin.beans.AopReplaceCut
import com.flyjingfish.android_aop_plugin.beans.ClassMethodRecord
import com.flyjingfish.android_aop_plugin.beans.ClassSuperInfo
import com.flyjingfish.android_aop_plugin.beans.MethodRecord
import com.flyjingfish.android_aop_plugin.beans.ReplaceInnerClassInfo
import com.flyjingfish.android_aop_plugin.beans.ReplaceMethodInfo
import com.flyjingfish.android_aop_plugin.config.AndroidAopConfig
import com.flyjingfish.android_aop_plugin.scanner_visitor.WovenIntoCode
import org.gradle.api.Project
import java.io.File
import java.util.jar.JarFile

object WovenInfoUtils {
    var isCompile = false
    var aopMethodCuts: HashMap = HashMap()
    var aopInstances: HashMap = HashMap()
    var aopMatchCuts: HashMap = HashMap()
    private var lastAopMatchCuts: HashMap = HashMap()
    var classPaths: HashSet = HashSet()
    var baseClassPaths: HashSet = HashSet()
    private var classNameMap: HashMap = HashMap()
    private var baseClassNameMap: HashMap = HashMap()
    private var classSuperListMap = HashMap()
    private var classSuperMap = HashMap()
    private val classSuperCacheMap = HashMap()
    private val classMethodRecords: HashMap> =
        HashMap()//类名为key,value为方法map集合
    private val invokeMethodCuts = mutableListOf()
    private val realInvokeMethodMap = HashMap()
    private val invokeMethodMap = HashMap()
    private val replaceMethodMap = HashMap()
    private val replaceMethodInfoMap = HashMap>()
    val replaceMethodInfoMapUse = HashMap()
    private val modifyExtendsClassMap = HashMap()
    private val allClassName = mutableSetOf()
    val aopCollectInfoMap = mutableMapOf()
    private val lastAopCollectInfoMap = mutableMapOf()
    val aopCollectClassMap = mutableMapOf?>()
    private val aopMethodCutInnerClassInfo = mutableMapOf()
    private val aopMethodCutInnerClassInfoClassName = mutableSetOf()
    private val aopMethodCutInnerClassInfoInvokeMethod = mutableSetOf()
    private val aopMethodCutInnerClassInfoInvokeClassName = mutableSetOf()
    private val aopMethodCutInnerClassInfoInvokeClassNameCount = mutableMapOf()
    fun addModifyExtendsClassInfo(targetClassName: String, extendsClassName: String) {
        modifyExtendsClassMap[targetClassName] = extendsClassName
        InitConfig.addModifyClassInfo(targetClassName, extendsClassName)
    }
    fun getModifyExtendsClass(targetClassName: String) :String?{
        return modifyExtendsClassMap[targetClassName]
    }
    fun verifyModifyExtendsClassInfo() {
        for (mutableEntry in modifyExtendsClassMap) {
            if (mutableEntry.value.instanceof(mutableEntry.key)){
                throw IllegalArgumentException("${mutableEntry.value} 不能继承 ${mutableEntry.key},或者其继承类不可以继承 ${mutableEntry.key}")
            }
        }
//        printLog("classMethodRecords=$classMethodRecords")
    }
    fun hasModifyExtendsClass():Boolean{
        return modifyExtendsClassMap.isNotEmpty()
    }
    fun addReplaceMethodInfo(filePath: String, replaceMethodInfo: ReplaceMethodInfo) {
        var infoMap = replaceMethodInfoMap[filePath]
        if (infoMap == null){
            infoMap = HashMap()
            replaceMethodInfoMap[filePath] = infoMap
        }
        infoMap[replaceMethodInfo.getReplaceKey()] = replaceMethodInfo
    }
    fun deleteReplaceMethodInfo(filePath: String) {
        replaceMethodInfoMap.remove(filePath)
    }
    fun makeReplaceMethodInfoUse() {
        replaceMethodInfoMapUse.clear()
        for (mutableEntry in replaceMethodInfoMap) {
            replaceMethodInfoMapUse.putAll(mutableEntry.value)
        }
    }
    fun getReplaceMethodInfoUse(key: String):ReplaceMethodInfo? {
        return replaceMethodInfoMapUse[key]
    }
    fun hasReplace():Boolean{
        return replaceMethodInfoMapUse.isNotEmpty()
    }
    fun addReplaceInfo(targetClassName: String,invokeClassName: String) {
        invokeMethodMap[invokeClassName] = targetClassName
        replaceMethodMap[targetClassName] = invokeClassName
    }

    fun addReplaceCut(aopReplaceCut: AopReplaceCut) {
        invokeMethodCuts.add(aopReplaceCut)
    }

    fun addRealReplaceInfo(targetClassName: String,invokeClassName: String) {
        realInvokeMethodMap[targetClassName] = invokeClassName
    }
    fun getRealReplaceInfo(targetClassName: String):String? {
       return realInvokeMethodMap[targetClassName]
    }
    fun containInvoke(className: String):Boolean{
        return invokeMethodMap.containsKey(className)
    }
    fun getTargetClassName(className: String):String?{
        return invokeMethodMap[className]
    }

    fun isReplaceMethod(className: String):Boolean{
        if (containReplace(className)){
            return false
        }
        for (mutableEntry in invokeMethodMap) {
            if (className.contains(mutableEntry.key)){
                return false
            }
        }
        return true
    }

    fun containReplace(className: String):Boolean{
        return replaceMethodMap.containsKey(className)
    }
    fun getReplaceClassName(className: String):String?{
        return replaceMethodMap[className]
    }
    fun addAnnoInfo(info: AopMethodCut) {
        aopMethodCuts[info.anno] = info
    }

    fun addAopInstance(key: String,className: String) {
        aopInstances[key] = className
    }

    fun isContainAnno(info: String): Boolean {
        val anno = "@" + info.substring(1, info.length).replace("/", ".").replace(";", "")
        return aopMethodCuts.contains(anno)
    }

    fun getAnnoInfo(info: String): AopMethodCut? {
        val anno = "@" + info.substring(1, info.length).replace("/", ".").replace(";", "")
        return aopMethodCuts[anno]
    }

    fun addMatchInfo(info: AopMatchCut) {
        //baseClassName -> cutClassName 防止被覆盖
        aopMatchCuts[info.cutClassName] = info
    }

    fun addClassMethodRecords(classMethodRecord: ClassMethodRecord) {
        var methodsRecord: HashMap? =
            classMethodRecords[classMethodRecord.classFile]
        if (methodsRecord == null) {
            methodsRecord = HashMap()
            classMethodRecords[classMethodRecord.classFile] = methodsRecord
        }
        val key = classMethodRecord.methodName.methodName + classMethodRecord.methodName.descriptor
        val oldRecord = methodsRecord[key]
        if (methodsRecord.contains(key)) {
            if (classMethodRecord.methodName.cutClassName.isNotEmpty()) {
                methodsRecord[key]?.cutClassName?.addAll(classMethodRecord.methodName.cutClassName)
                methodsRecord[key]?.cutClassName?.let {
                    classMethodRecord.methodName.cutClassName.addAll(it)
                }
                methodsRecord[key] = classMethodRecord.methodName
            }
        } else {
            methodsRecord[key] = classMethodRecord.methodName
        }
        oldRecord?.cutInfo?.let {
            methodsRecord[key]?.cutInfo?.putAll(it)
        }
        methodsRecord[key]?.cutInfo?.let {
            it.putAll(classMethodRecord.methodName.cutInfo)
        }
    }

    fun deleteClassMethodRecord(key: String) {
        classMethodRecords.remove(key)
    }

    fun getClassMethodRecord(classFile: String): HashMap? {
        return classMethodRecords[classFile]
    }


    fun addClassPath(classPath: String) {
        classPaths.add(classPath)
    }
    private fun clear() {
        invokeMethodMap.clear()
        replaceMethodMap.clear()
        replaceMethodInfoMapUse.clear()
        modifyExtendsClassMap.clear()
        invokeMethodCuts.clear()
        realInvokeMethodMap.clear()
        invokeMethodCutCache = null
        aopMethodCuts.clear()
        aopInstances.clear()
        aopMethodCutInnerClassInfo.clear()
        aopMethodCutInnerClassInfoClassName.clear()
        aopMethodCutInnerClassInfoInvokeMethod.clear()
        aopMethodCutInnerClassInfoInvokeClassName.clear()
        aopMethodCutInnerClassInfoInvokeClassNameCount.clear()
//        aopCollectClassMap.clear()
        if (!AndroidAopConfig.increment) {
            aopMatchCuts.clear()
            lastAopMatchCuts.clear()
            aopCollectInfoMap.clear()
            classPaths.clear()
            baseClassPaths.clear()
            classNameMap.clear()
            baseClassNameMap.clear()
            classSuperListMap.clear()
            classSuperMap.clear()
            classSuperCacheMap.clear()
            classMethodRecords.clear()
        } else {

            classSuperCacheMap.clear()
            classSuperCacheMap.putAll(classSuperMap)

            lastAopMatchCuts.clear()
            lastAopMatchCuts.putAll(aopMatchCuts)

            lastAopCollectInfoMap.clear()
            lastAopCollectInfoMap.putAll(aopCollectInfoMap)

            aopCollectInfoMap.clear()
            aopMatchCuts.clear()
            classPaths.clear()
//        classMethodRecords.clear()
            classNameMap.clear()
//        classSuperList.clear()
            classSuperMap.clear()
        }
    }

    fun addClassName(classPath: String) {
        val key = Utils.slashToDot(classPath).replace(".class", "").replace("$", ".")
        val value = Utils.slashToDot(classPath).replace(".class", "")
        classNameMap[key] = value
    }

    private fun addBaseClassName(classPath: String) {
        val key = Utils.slashToDot(classPath).replace(".class", "").replace("$", ".")
        val value = Utils.slashToDot(classPath).replace(".class", "")
        baseClassNameMap[key] = value
    }

    fun getClassString(key: String): String? {
        return classNameMap[key]
    }

    fun addClassSuper(file: String, classSuper: ClassSuperInfo) {
        classSuperListMap[classSuper.className] = classSuper
        classSuperMap[file] = classSuper.className
    }

    fun isLeaf(className: String): Boolean {
        val set = classSuperListMap.entries
        for (classSuperInfo in set) {
            if (classSuperInfo.value.superName == className || (classSuperInfo.value.interfaces?.contains(
                    className
                ) == true)
            ) {
                return false
            }
        }
        return true
    }

    private fun removeDeletedClass(key: String) {
//        printLog("removeDeletedClass= key =$key,value=${classSuperListMap[key]}")
        classSuperListMap.remove(key)
    }

    /**
     * 删除访问到的文件,最后剩下就是真正删除了的文件
     */
    fun removeClassCache(key: String) {
        classSuperCacheMap.remove(key)
    }

    fun removeDeletedClass() {
        if (!AndroidAopConfig.increment || isCompile){
            return
        }
        val set = classSuperCacheMap.entries
        for (mutableEntry in set) {
            removeDeletedClass(mutableEntry.value)
        }
    }

    fun removeDeletedClassMethodRecord() {
        if (!AndroidAopConfig.increment || isCompile){
            return
        }
        val set = classSuperCacheMap.entries
        for (mutableEntry in set) {
            classMethodRecords.remove(mutableEntry.key)
        }
    }

    fun addBaseClassInfo(project: Project) {
        val androidConfig = AndroidConfig(project)
        val list: List = androidConfig.getBootClasspath()
//        printLog("Scan to classPath [${list}]")
//        printLog("Scan to classPath [${classPaths}]")
        clear()

        val classPaths: HashSet = HashSet()
        for (file in list) {
            classPaths.add(file.absolutePath)
        }

        if (classPaths != baseClassPaths) {
            baseClassPaths.clear()
            baseClassPaths.addAll(classPaths)

            baseClassNameMap.clear()
            fillClassNameMap(list)
        }
        if (baseClassNameMap.isEmpty()) {
            fillClassNameMap(list)
        }
        WovenInfoUtils.classPaths.addAll(classPaths)
        classNameMap.putAll(baseClassNameMap)
    }

    private fun fillClassNameMap(list: List) {
        for (file in list) {
            try {
                val jarFile = JarFile(file)
                val enumeration = jarFile.entries()
                while (enumeration.hasMoreElements()) {
                    val jarEntry = enumeration.nextElement()
                    try {
                        val entryName = jarEntry.name
                        if (entryName.endsWith(Utils._CLASS)) {
                            addBaseClassName(entryName)
                        }
                    } catch (_: Exception) {

                    }
                }
                jarFile.close()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

    fun aopMatchsChanged(): Boolean {
        return lastAopMatchCuts != aopMatchCuts || lastAopCollectInfoMap != aopCollectInfoMap
    }

    fun isHasExtendsReplace():Boolean{
        if (invokeMethodCutCache == null){
            invokeMethodCutCache = invokeMethodCuts.filter {
                it.matchType != AopMatchCut.MatchType.SELF.name
            }.toMutableList()
        }
        return !invokeMethodCutCache.isNullOrEmpty()
    }

    private var invokeMethodCutCache : MutableList?= null
    fun addExtendsReplace(className:String){
        if (!isHasExtendsReplace()){
            return
        }
        val ctClass = try {
            ClassPoolUtils.classPool?.getCtClass(className) ?: return
        } catch (e: Exception) {
            return
        }

        val realClsName: String
        val superName: String?
        val interfaces: Array
        try {
            realClsName = Utils.dotToSlash(className)
            superName = ctClass.superclass.name
            val interfacesCls = ctClass.interfaces
            interfaces = arrayOfNulls(interfacesCls.size)
            if (interfacesCls != null){
                for ((index,interfacesCl) in interfacesCls.withIndex()) {
                    interfaces[index] = interfacesCl.name
                }
            }
        } catch (e: Exception) {
            return
        }

        invokeMethodCutCache?.forEach { aopReplaceCut ->
            val target = Utils.dotToSlash(aopReplaceCut.targetClassName)
            if (AopMatchCut.MatchType.SELF.name != aopReplaceCut.matchType) {

                val excludeClazz = aopReplaceCut.excludeClass
                var exclude = false
                var isDirectExtends = false
                if (excludeClazz != null) {
                    val clsName = Utils.slashToDotClassName(className)
                    for (clazz in excludeClazz) {
                        if (clsName == Utils.slashToDotClassName(clazz)) {
                            exclude = true
                            break
                        }
                    }
                }
                if (!exclude) {
                    var isImplementsInterface = false
                    if (interfaces.isNotEmpty()) {
                        for (anInterface in interfaces) {
                            val inter = Utils.slashToDotClassName(anInterface!!)
                            if (inter == Utils.slashToDotClassName(aopReplaceCut.targetClassName)) {
                                isImplementsInterface = true
                                break
                            }
                        }
                    }
                    if (isImplementsInterface || Utils.slashToDotClassName(aopReplaceCut.targetClassName) == Utils.slashToDotClassName(
                            superName!!
                        )
                    ) {
                        isDirectExtends = true
                    }
                    //isDirectExtends 为true 说明是直接继承
                    if (AopMatchCut.MatchType.DIRECT_EXTENDS.name == aopReplaceCut.matchType) {
                        if (isDirectExtends) {
                            addRealReplaceInfo(realClsName,target)
                        }
                    } else if (AopMatchCut.MatchType.LEAF_EXTENDS.name == aopReplaceCut.matchType) {
                        var isExtends = false
                        if (isDirectExtends) {
                            isExtends = true
                        } else {
                            val clsName = Utils.slashToDotClassName(className)
                            val parentClsName = aopReplaceCut.targetClassName
                            if (clsName != Utils.slashToDotClassName(parentClsName)) {
                                isExtends = clsName.instanceof(
                                    Utils.slashToDotClassName(parentClsName)
                                )
                            }
                        }
                        if (isExtends && isLeaf(className)) {
                            addRealReplaceInfo(realClsName,target)
                        }
                    } else {
                        if (isDirectExtends) {
                            addRealReplaceInfo(realClsName,target)
                        } else {
                            val clsName = Utils.slashToDotClassName(className)
                            val parentClsName = aopReplaceCut.targetClassName
                            if (clsName != Utils.slashToDotClassName(parentClsName)) {
                                val isInstanceof = clsName.instanceof(
                                    Utils.slashToDotClassName(parentClsName)
                                )
                                if (isInstanceof) {
                                    addRealReplaceInfo(realClsName,target)
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    fun addCollectConfig(aopCollectCut: AopCollectCut){
        aopCollectInfoMap[aopCollectCut.getKey()] = aopCollectCut
    }

    fun addCollectClass(aopCollectClass: AopCollectClass){
        var set = aopCollectClassMap[aopCollectClass.invokeClassName]
        if (set == null){
            set = mutableMapOf()
            aopCollectClassMap[aopCollectClass.invokeClassName] = set
        }
        set[aopCollectClass.getKey()] = aopCollectClass
    }
    fun aopCollectChanged(isClear:Boolean) {
        if (isClear){
            aopCollectClassMap.clear()
            return
        }
        val iterator = aopCollectClassMap.iterator()
        while (iterator.hasNext()){
            val item = iterator.next()
            val key = item.key
            val value = item.value
            var containInvokeClassName = false;
            for (mutableEntry in aopCollectInfoMap) {
                if (mutableEntry.value.invokeClassName == key){
                    containInvokeClassName = true
                    break
                }
            }
            if (!containInvokeClassName){
                iterator.remove()
            }else{
                value?.let {
                    val itIterator = it.iterator()
                    while (itIterator.hasNext()){
                        val itItem = itIterator.next()
                        var itContain = false
                        for (mutableEntry in aopCollectInfoMap) {
                            if (mutableEntry.value.invokeMethod == itItem.value.invokeMethod
                                && mutableEntry.value.collectClassName == itItem.value.collectClassName
                                && mutableEntry.value.isClazz == itItem.value.isClazz
                                && mutableEntry.value.regex == itItem.value.regex
                                && mutableEntry.value.collectType == itItem.value.collectType){
                                itContain = true
                                break
                            }
                        }

                        if (!itContain){
                            itIterator.remove()
                        }
                    }
                }

            }

        }
    }
    fun initAllClassName(){
        allClassName.clear()
        classNameMap.forEach{(_,value)->
            allClassName.add(value)
        }
    }
    private const val CHECK_CLASS_HINT = "AndroidAOP提示:由于您切换了debugMode模式,请clean项目。"
    fun checkNoneInvokeClass(className:String){
        if (!ClassFileUtils.reflectInvokeMethod && !allClassName.contains(className) && !ClassFileUtils.debugMode){
            throw RuntimeException(CHECK_CLASS_HINT)
        }
    }

    fun checkHasInvokeClass(className:String){
        if (!ClassFileUtils.reflectInvokeMethod && allClassName.contains(className) && !ClassFileUtils.debugMode){
            throw RuntimeException(CHECK_CLASS_HINT)
        }
    }

    fun containsInvokeClass(className:String):Boolean{
        return allClassName.contains(className)
    }

    fun checkHasInvokeJson(project: Project,variant:String){
        if (!ClassFileUtils.debugMode){
            val cacheJsonFile = File(Utils.invokeJsonFile(project,variant))
            if (cacheJsonFile.exists()){
                throw RuntimeException(CHECK_CLASS_HINT)
            }
        }
    }

    fun checkLeafConfig(isApp:Boolean){
        val verifyLeafExtends = AndroidAopConfig.verifyLeafExtends
        if (!verifyLeafExtends && isApp){
            fun getHintText(location:String):String{
                return "您已在 androidAopConfig 设置了 verifyLeafExtends = false,$location 就不能再设置 LEAF_EXTENDS 类型了,如需使用请打开此项配置"
            }
            //@AndroidAopMatchClassMethod
            aopMatchCuts.forEach {(_,cutConfig) ->
                if (cutConfig.matchType == AopMatchCut.MatchType.LEAF_EXTENDS.name){
                    throw IllegalArgumentException(getHintText(cutConfig.cutClassName))
                }
            }
            //@AndroidAopReplaceClass
            invokeMethodCuts.forEach { cutConfig ->
                if (cutConfig.matchType == AopMatchCut.MatchType.LEAF_EXTENDS.name){
                    throw IllegalArgumentException(getHintText(cutConfig.invokeClassName))
                }
            }
            //@AndroidAopCollectMethod
            aopCollectInfoMap.forEach {(_,cutConfig) ->
                if (cutConfig.collectType == AopCollectCut.CollectType.LEAF_EXTENDS.name){
                    throw IllegalArgumentException(getHintText("${cutConfig.invokeClassName}.${cutConfig.invokeMethod}"))
                }
            }
        }
    }

    fun addAopMethodCutInnerClassInfo(className:String,
                                      oldMethodName: String,
                                      oldDescriptor: String){
        val classNameMd5 = Utils.slashToDot(className).computeMD5()
        val newMethodName = "$oldMethodName$$$classNameMd5${WovenIntoCode.METHOD_SUFFIX}"
        if (oldDescriptor.endsWith("Lkotlin/coroutines/Continuation;)Ljava/lang/Object;")) {
            val key = "$className&$oldMethodName&$oldDescriptor"
            aopMethodCutInnerClassInfo[key] = ReplaceInnerClassInfo(className,oldMethodName,oldDescriptor,newMethodName)
            aopMethodCutInnerClassInfoClassName.add(className)
        }
    }

    fun getAopMethodCutInnerClassInfo(className:String,
                                      oldMethodName: String,
                                      oldDescriptor: String,
                                      innerClassName:String,
                                      innerSuperClassName:String):ReplaceInnerClassInfo?{
        return if (innerClassName.startsWith("$className$$oldMethodName")
            && innerClassName != className
            && innerSuperClassName != "kotlin/coroutines/jvm/internal/SuspendLambda"){
            val key = "$className&$oldMethodName&$oldDescriptor"
            aopMethodCutInnerClassInfo[key]
        }else{
            null
        }
    }

    fun isHasAopMethodCutInnerClassInfo(className:String):Boolean{
        for (s in aopMethodCutInnerClassInfoClassName) {
            if (className.startsWith("$s$")){
                return true
            }
        }
        return false
    }

    fun addAopMethodCutInnerClassInfoInvokeMethod(className:String,
                                                  newMethodName: String,
                                                  descriptor: String){
        if (descriptor.endsWith("Lkotlin/coroutines/Continuation;)Ljava/lang/Object;")) {
            val key = "$className&$newMethodName&$descriptor"
            aopMethodCutInnerClassInfoInvokeMethod.add(key)
        }
    }

    fun isHasAopMethodCutInnerClassInfoInvokeMethod(className:String,
                                                  newMethodName: String,
                                                  descriptor: String):Boolean{
        val key = "$className&$newMethodName&$descriptor"
        return aopMethodCutInnerClassInfoInvokeMethod.contains(key)
    }

    fun addAopMethodCutInnerClassInfoInvokeClassName(className:String,count:Int){
        aopMethodCutInnerClassInfoInvokeClassName.add(className)
        aopMethodCutInnerClassInfoInvokeClassNameCount[className] = count
    }

    fun getAopMethodCutInnerClassInfoInvokeClassInfo(className:String): HashMap?{
        return if (aopMethodCutInnerClassInfo.isNotEmpty() && aopMethodCutInnerClassInfoInvokeClassName.contains(className)){
            val methodsRecord: HashMap = HashMap()
            methodsRecord[className] = MethodRecord("invokeSuspend",
                "(Ljava/lang/Object;)Ljava/lang/Object;")
//            methodsRecord[className] = MethodRecord("invoke",
//                "(Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;",
//                null)
            methodsRecord
        }else{
            null
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy