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

com.didiglobal.booster.transform.logcat.LogcatTransformer.kt Maven / Gradle / Ivy

The newest version!
package com.didiglobal.booster.transform.logcat

import com.didiglobal.booster.kotlinx.asIterable
import com.didiglobal.booster.kotlinx.touch
import com.didiglobal.booster.transform.TransformContext
import com.didiglobal.booster.transform.asm.ClassTransformer
import com.didiglobal.booster.transform.logcat.Build.*
import com.google.auto.service.AutoService
import org.objectweb.asm.Opcodes.GETSTATIC
import org.objectweb.asm.Opcodes.INVOKESTATIC
import org.objectweb.asm.Opcodes.INVOKEVIRTUAL
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.FieldInsnNode
import org.objectweb.asm.tree.MethodInsnNode
import java.io.PrintWriter

private const val LOGCAT = "android/util/Log"
private const val THROWABLE = "java/lang/Throwable"
private const val SYSTEM = "java/lang/System"
private const val INSTRUMENT = "com/didiglobal/booster/instrument/"
private const val SHADOW_LOG = "${INSTRUMENT}ShadowLog"
private const val SHADOW_SYSTEM = "${INSTRUMENT}ShadowSystem"
private const val SHADOW_THROWABLE = "${INSTRUMENT}ShadowThrowable"
private val SHADOW_LOG_METHODS = setOf("v", "d", "i", "w", "e", "wtf", "println")

/**
 * @author neighbWang
 */
@AutoService(ClassTransformer::class)
class LogcatTransformer : ClassTransformer {

    private lateinit var logger: PrintWriter

    override val name: String = ARTIFACT

    override fun onPreTransform(context: TransformContext) {
        this.logger = getReport(context, "report.txt").touch().printWriter()
    }

    override fun onPostTransform(context: TransformContext) {
        this.logger.close()
    }

    override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
        if (context.isDebuggable || klass.name.startsWith(INSTRUMENT)) {
            return klass
        }
        klass.methods.forEach { method ->
            method.instructions?.iterator()?.asIterable()?.filter {
                when (it.opcode) {
                    INVOKESTATIC -> (it as MethodInsnNode).owner == LOGCAT && SHADOW_LOG_METHODS.contains(it.name)
                    INVOKEVIRTUAL -> (it as MethodInsnNode).name == "printStackTrace" && it.desc == "()V" && context.klassPool.get(THROWABLE).isAssignableFrom(it.owner)
                    GETSTATIC -> (it as FieldInsnNode).owner == SYSTEM && (it.name == "out" || it.name == "err")
                    else -> false
                }
            }?.forEach {
                when (it.opcode) {
                    INVOKESTATIC -> {
                        logger.println(" * ${(it as MethodInsnNode).owner}.${it.name}${it.desc} => $SHADOW_LOG.${it.name}${it.desc}: ${klass.name}.${method.name}${method.desc}")
                        it.owner = SHADOW_LOG
                    }
                    INVOKEVIRTUAL -> {
                        logger.println(" * ${(it as MethodInsnNode).owner}.${it.name}${it.desc} => $SHADOW_LOG.${it.name}${it.desc}: ${klass.name}.${method.name}${method.desc}")
                        it.apply {
                            itf = false
                            owner = SHADOW_THROWABLE
                            desc = "(Ljava/lang/Throwable;)V"
                            opcode = INVOKESTATIC
                        }
                    }
                    GETSTATIC -> {
                        logger.println(" * ${(it as FieldInsnNode).owner}.${it.name}${it.desc} => $SHADOW_LOG.${it.name}${it.desc}: ${klass.name}.${method.name}${method.desc}")
                        it.owner = SHADOW_SYSTEM
                    }
                }
            }
        }
        return klass
    }

}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy