com.zhugeioanalytics.android.plugin.ZhugeioAnalyticsClassVisitor.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of android-gradle-plugin Show documentation
Show all versions of android-gradle-plugin Show documentation
The official Android SDK Plugin for Zhugeio.
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()
}
}
}