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

com.zhugeioanalytics.android.plugin.ZhugeioAnalyticsClassVisitor.groovy Maven / Gradle / Ivy

There is a newer version: 2.1.1
Show newest version
package com.zhugeioanalytics.android.plugin

import com.zhugeioanalytics.android.plugin.visitor.ZhugeioAnalyticsAutoTrackMethodVisitor
import com.zhugeioanalytics.android.plugin.visitor.ZhugeioAnalyticsDefaultMethodVisitor
import com.zhugeioanalytics.android.plugin.visitor.ZhugeioAnalyticsWebViewMethodVisitor
import org.objectweb.asm.*

class ZhugeioAnalyticsClassVisitor extends ClassVisitor {
    private String mClassName
    private String mSuperName
    private String[] mInterfaces
    private HashSet visitedFragMethods = new HashSet<>()// 无需判空
    private ClassVisitor classVisitor

    private ZhugeioAnalyticsTransformHelper transformHelper

    private ClassNameAnalytics classNameAnalytics

    private ArrayList methodCells = new ArrayList<>()

    private int version

    private HashMap mLambdaMethodCells = new HashMap<>()

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone()
    }

    ZhugeioAnalyticsClassVisitor(final ClassVisitor classVisitor, ClassNameAnalytics classNameAnalytics, ZhugeioAnalyticsTransformHelper transformHelper) {
        super(ZhugeioAnalyticsUtil.ASM_VERSION, classVisitor)
        this.classVisitor = classVisitor
        this.classNameAnalytics = classNameAnalytics
        this.transformHelper = transformHelper
    }

    private
    static void visitMethodWithLoadedParams(MethodVisitor methodVisitor, int opcode, String owner, String methodName, String methodDesc, int start, int count, List paramOpcodes) {
        for (int i = start; i < start + count; i++) {
            methodVisitor.visitVarInsn(paramOpcodes[i - start], i)
        }
        methodVisitor.visitMethodInsn(opcode, owner, methodName, methodDesc, false)
    }

    /**
     * 该方法是当扫描类时第一个拜访的方法,主要用于类声明使用
     * @param version 表示类版本:51,表示 “.class” 文件的版本是 JDK 1.7
     * @param access 类的修饰符:修饰符在 ASM 中是以 “ACC_” 开头的常量进行定义。
     *                          可以作用到类级别上的修饰符有:ACC_PUBLIC(public)、ACC_PRIVATE(private)、ACC_PROTECTED(protected)、
     *                          ACC_FINAL(final)、ACC_SUPER(extends)、ACC_INTERFACE(接口)、ACC_ABSTRACT(抽象类)、
     *                          ACC_ANNOTATION(注解类型)、ACC_ENUM(枚举类型)、ACC_DEPRECATED(标记了@Deprecated注解的类)、ACC_SYNTHETIC
     * @param name 类的名称:通常我们的类完整类名使用 “org.test.mypackage.MyClass” 来表示,但是到了字节码中会以路径形式表示它们 “org/test/mypackage/MyClass” 。
     *                      值得注意的是虽然是路径表示法但是不需要写明类的 “.class” 扩展名。
     * @param signature 表示泛型信息,如果类并未定义任何泛型该参数为空
     * @param superName 表示所继承的父类:由于 Java 的类是单根结构,即所有类都继承自 java.lang.Object。 因此可以简单的理解为任何类都会具有一个父类。
     *                  虽然在编写 Java 程序时我们没有去写 extends 关键字去明确继承的父类,但是 JDK在编译时 总会为我们加上 “ extends Object”。
     * @param interfaces 表示类实现的接口,在 Java 中类是可以实现多个不同的接口因此此处是一个数组。
     */
    @Override
    void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        mClassName = name
        mSuperName = superName
        mInterfaces = interfaces
        this.version = version
        super.visit(version, access, name, signature, superName, interfaces)
        if (Logger.debug) {
            Logger.info("开始扫描类:${mClassName}")
            Logger.info("类详情:version=${version};\taccess=${Logger.accCode2String(access)};\tname=${name};\tsignature=${signature};\tsuperName=${superName};\tinterfaces=${interfaces.toArrayString()}\n")
        }
    }


    /**
     * 该方法是当扫描器完成类扫描时才会调用,如果想在类中追加某些方法,可以在该方法中实现。
     */
    @Override
    void visitEnd() {
        super.visitEnd()

        if (ZhugeioAnalyticsUtil.isInstanceOfFragment(mSuperName)) {
            MethodVisitor mv
            // 添加剩下的方法,确保super.onHiddenChanged(hidden);等先被调用
            Iterator> iterator = ZhugeioAnalyticsHookConfig.FRAGMENT_METHODS.entrySet().iterator()
            while (iterator.hasNext()) {
                Map.Entry entry = iterator.next()
                String key = entry.getKey()
                ZhugeioAnalyticsMethodCell methodCell = entry.getValue()
                if (visitedFragMethods.contains(key)) {
                    continue
                }
                mv = classVisitor.visitMethod(Opcodes.ACC_PUBLIC, methodCell.name, methodCell.desc, null, null)
                mv.visitCode()
                // call super
                visitMethodWithLoadedParams(mv, Opcodes.INVOKESPECIAL, mSuperName, methodCell.name, methodCell.desc, methodCell.paramsStart, methodCell.paramsCount, methodCell.opcodes)
                // call injected method
                visitMethodWithLoadedParams(mv, Opcodes.INVOKESTATIC, ZhugeioAnalyticsHookConfig.ZHUGEIO_ANALYTICS_API, methodCell.agentName, methodCell.agentDesc, methodCell.paramsStart, methodCell.paramsCount, methodCell.opcodes)
                mv.visitInsn(Opcodes.RETURN)
                mv.visitMaxs(methodCell.paramsCount, methodCell.paramsCount)
                mv.visitEnd()
                mv.visitAnnotation("Lcom/zhuge/analysis/util/ZhugeioInstrumented;", false)
            }
        }

        for (cell in methodCells) {
            transformHelper.zhugeioAnalyticsHookConfig."${cell.agentName}"(classVisitor, cell)
        }
        if (Logger.debug) {
            Logger.info("结束扫描类:${mClassName}\n")
        }
    }


    @Override
    FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
        if (classNameAnalytics.isZhugeioAPI) {
            if ('VERSION' == name) {
                String version = (String) value
                if (ZhugeioAnalyticsUtil.compareVersion(ZhugeioAnalyticsTransform.MIN_SDK_VERSION, version) > 0) {
                    String errMessage = "你目前集成的诸葛 io 埋点 SDK 版本号为 v${version}"
                    Logger.error(errMessage)
                    throw new Error(errMessage)
                }
            } else if ('MIN_PLUGIN_VERSION' == name) {
                String minPluginVersion = (String) value
                if (minPluginVersion != "" && minPluginVersion != null) {
                    if (ZhugeioAnalyticsUtil.compareVersion(ZhugeioAnalyticsTransform.VERSION, minPluginVersion) < 0) {
                        String errMessage = "你目前集成的诸葛 io 插件版本号为 v${ZhugeioAnalyticsTransform.VERSION}"
                        Logger.error(errMessage)
                        throw new Error(errMessage)
                    }
                }
            }
        }
        return super.visitField(access, name, descriptor, signature, value)
    }

    /**
     * 该方法是当扫描器扫描到类的方法时进行调用
     * @param access 表示方法的修饰符
     * @param name 表示方法名,在 ASM 中 “visitMethod” 方法会处理(构造方法、静态代码块、私有方法、受保护的方法、共有方法、native类型方法)。
     *                  在这些范畴中构造方法的方法名为 “”,静态代码块的方法名为 “”。
     * @param desc 表示方法签名,方法签名的格式如下:“(参数列表)返回值类型”
     * @param signature 凡是具有泛型信息的方法,该参数都会有值。并且该值的内容信息基本等于第三个参数的拷贝,只不过不同的是泛型参数被特殊标记出来
     * @param exceptions 用来表示将会抛出的异常,如果方法不会抛出异常,则该参数为空
     * @return
     */
    @Override
    MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        for (methodCell in classNameAnalytics.methodCells) {
            if (methodCell.name == name && methodCell.desc == desc) {
                methodCells.add(methodCell)
                return null
            }
        }

        MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, signature, exceptions)
        if (transformHelper.extension != null && transformHelper.extension.autoHandleWebView && transformHelper.urlClassLoader != null) {
            methodVisitor = new ZhugeioAnalyticsWebViewMethodVisitor(methodVisitor, access, name, desc, transformHelper, mClassName, mSuperName)
        }
        ZhugeioAnalyticsDefaultMethodVisitor zhugeioAnalyticsDefaultMethodVisitor = new ZhugeioAnalyticsAutoTrackMethodVisitor(methodVisitor, access, name, desc,
                transformHelper,mClassName,mSuperName,mLambdaMethodCells,classNameAnalytics)
        //如果java version 为1.5以前的版本,则使用JSRInlinerAdapter来删除JSR,RET指令
        if (version <= Opcodes.V1_5) {
            return new ZhugeioAnalyticsJSRAdapter(ZhugeioAnalyticsUtil.ASM_VERSION, zhugeioAnalyticsDefaultMethodVisitor, access, name, desc, signature, exceptions)
        }
        return zhugeioAnalyticsDefaultMethodVisitor
    }

    /**
     * 获取方法参数下标为 index 的对应 ASM index
     * @param types 方法参数类型数组
     * @param index 方法中参数下标,从 0 开始
     * @param isStaticMethod 该方法是否为静态方法
     * @return 访问该方法的 index 位参数的 ASM index
     */
    int getVisitPosition(Type[] types, int index, boolean isStaticMethod) {
        if (types == null || index < 0 || index >= types.length) {
            throw new Error("getVisitPosition error")
        }
        if (index == 0) {
            return isStaticMethod ? 0 : 1
        } else {
            return getVisitPosition(types, index - 1, isStaticMethod) + types[index - 1].getSize()
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy