一、背景及收益
升级sdk或更新依赖库后,可能因为库之间依赖的版本号不同,API有变动时会报:NoSuchMethodError 等错误
二、ByteX实现原理
ByteX是一个基于gradle transform api和ASM的字节码而实现的
三、bytex-referCheck 检测插件的整体实现思路:
- 将所有的子插件注册到宿主插件中,并给每个子插件绑定一个TransformFlow【默认为全局MainTransformFlow】
- 宿主插件的Transform方法中遍历执行每个子插件的TransformFlow执行run方法对.class进行处理:
首先遍历工程中所有的构建产物(.class)和 遍历android.jar里的所有class文件生成类结构图【map:key(className),value(类/接口node)】;
然后再遍历一次工程中所有的构建产物,将每个class文件传给bytex-referCheck子插件的transform方法处理 - 子插件ReferCheckPlugin的transform方法中结合第一次遍历的生成类图,与 ASM ClassVisitor 进行合法性验证
四、名词解释
- traverse过程:遍历一次工程中所有的构建产物(一般来说是class文件),单纯做遍历分析,不对输入文件做修改;
- traverseAndroidJar过程:遍历android.jar里的所有class文件 (哪个版本的android.jar由工程中的target api决定),主要是为了形成完整的类图.
- transform:再遍历一次工程中所有的构建产物,并对class文件做处理后输出(可能是直接回写到本地,也可能作为下一个处理过程的输入)
- 类图Graph:Graph里维护一个map集合 key为类名,value 是类或者接口节点
五、关键过程及代码
5-1:将子插件注册到bytex宿主插件中
首先在子插件的apply()方法中,将子插件注册到bytex宿主插件扩展类ByteXExtension中。
AbsPlugin.java
public final void apply(@NotNull Project project) {
...
ByteXExtension byteX = project.getExtensions().getByType(ByteXExtension.class);
// 向宿主插件 byteX 注册子插件,那么 宿主插件会持有 所有 注册的子插件的引用,通过 ByteXExtension 获取
byteX.registerPlugin(this);
...
在bytex宿主插件中,获取宿主插件扩展ByteXExtension,并将扩展传入ByteXTransform
public class ByteXPlugin implements Plugin<Project> {
@Override
public void apply(@NotNull Project project) {
// 获取 Android 为 App 提供的扩展属性 AppExtension 实例
AppExtension android = project.getExtensions().getByType(AppExtension.class);
// 获取 ByteX 自身创建的扩展属性 ByteXExtension 实例
ByteXExtension extension = project.getExtensions().create("ByteX", ByteXExtension.class);
// 注册 ByteXTransform 实例。ByteXTransform 继承了抽象类 CommonTransform,其实现了关键的 transform 方法
android.registerTransform(new ByteXTransform(new Context(project, android, extension)));
}
}
5-2:为每个子插件注册TransformFlow并执行run方法
从扩展中拿到所有子插件
/**
* 从扩展中获取 所有子插件的引用
* @return
*/
@Override
protected List<IPlugin> getPlugins() {
if (!context.extension.isEnable()) {
return Collections.emptyList();
}
return context.extension.getPlugins();
}
遍历插件注册TransformFlow
CommonTransform.java
private void transformInternal(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
...
plugins.forEach(plugin -> {
manager.bind(new FlowBinder() {
@Override
public TransformFlow bind(TransformFlowerManager manager) {
// 调用子插件的registerTransformFlow 方法 传入全局公共的transform流MainTransformFlow
return plugin.registerTransformFlow(manager.getCommonFlow(), transformContext);
}
});
}
);
...
}
5-3: 公共 MainTransformFlow.runTransform()遍历产物构建类图,以 遍历工程构建产物为例
遍历工程产物
traverseArtifactOnly(getProcessors(Process.TRAVERSE, new ClassFileAnalyzer(context, Process.TRAVERSE, graphBuilder, new ArrayList<>(handlers))));
getProcessors
@param process:Process.TRAVERSE遍历
@param fileHandler ClassFileAnalyzer类文件解析的
返回的是FileProcessor[]
/**
* 根据传入的状态 返回不同的文件处理器【4 种自定义的】 数组
* @param process 不同的处理状态
* @param fileHandler 根据不同处理状态 传入 不同的文件分析器 【FileHandler 子类: 1 ClassFileAnalyzer 类文件分析器 2 ClassFileTransformer 类文件转换器】
* @return
*/
private FileProcessor[] getProcessors(Process process, FileHandler fileHandler) {
// 在 traverse(遍历) 和 transform(转换) 的过程中,加入自定义的 FileProcessor,提供更大的灵活性
List<FileProcessor> processors = handlers.stream()
.flatMap((Function<MainProcessHandler, Stream<FileProcessor>>) handler -> handler.process(process).stream())
.collect(Collectors.toList());
switch (process) {
// 增量遍历状态
case TRAVERSE_INCREMENTAL:
// FilterFileProcessor:按照指定的条件过滤掉不需要的 FileData,此时的过滤条件是文件的状态不是 Status.NOTCHANGED
processors.add(0, new FilterFileProcessor(fileData -> fileData.getStatus() != Status.NOTCHANGED));
// IncrementalFileProcessor 增量文件处理器,其中包含一个 ClassFileProcessor(FileHandler) 参数,用于对文件解析使用
processors.add(new IncrementalFileProcessor(new ArrayList<>(handlers), ClassFileProcessor.newInstance(fileHandler)));
break;
case TRAVERSE:
case TRAVERSE_ANDROID:
case TRANSFORM:
// ClassFileProcessor 类文件处理器,其中包含一个 ClassFileProcessor(FileHandler) 参数,用于对文件解析使用。
processors.add(ClassFileProcessor.newInstance(fileHandler));
// FilterFileProcessor 按照指定的条件过滤掉不需要的 FileData,此时的过滤条件是文件的状态不是 Status.NOTCHANGED 和 Status.REMOVED
processors.add(0, new FilterFileProcessor(fileData -> fileData.getStatus() != Status.NOTCHANGED && fileData.getStatus() != Status.REMOVED));
break;
default:
throw new RuntimeException("Unknow Process:" + process);
}
return processors.toArray(new FileProcessor[0]);
}
类型为:Process.TRAVERSE 则返回的是FileProcessor[]
index= 0-> FilterFileProcessor
else ->ClassFileProcessor
然后执行的TransformEngine,traverseOnly()方法
/**
* cpu密集型 线程池 执行 遍历 任务
* @param processors
*/
public void traverseOnly(FileProcessor... processors) {
Schedulers.FORKJOINPOOL().invoke(new PerformTraverseTask(context.allFiles(), getProcessorList(processors)));
}
其中 传入的
执行的PerformTraverseTask的compute()方法
@Override
protected void compute() {
List<FileTraverseTask> tasks = source.map(cache -> new FileTraverseTask(cache, processors)).collect(Collectors.toList());
// 回调所有的 FileTraverseTask 实例的 compute 方法
invokeAll(tasks);
}
执行的是 FileTraverseTask.compute -> TraverseTask.compute-> ProcessorChain 链式任务
ProcessorChain.proceed方法
@Override
public Output proceed(Input input) throws IOException {
if (index >= processors.size()) throw new AssertionError();
// 1 index = 0 时 会从 processors 处理器列表中获取第一个处理器—FilterFileProcessor,并调用它的 process 方法
// 2 index = 1 -> processors.size()-1 之前 时 是 ClassFileProcessor 则会执行 ClassFileProcessor.process()
// 3 index = processors.size()-1 时 为 BackupFileProcessor 则执行 BackupFileProcessor.process()
FileProcessor next = processors.get(index);
return next.process(new ProcessorChain(processors, input, index + 1));
}
遍历任务的话主要在ClassFileProcessor.process() 中
ClassFileProcessor.process
@Override
public Output process(Chain chain) throws IOException {
Input input = chain.input();
FileData fileData = input.getFileData();
if (fileData.getRelativePath().endsWith(".class")) {
// case 1、MainTransformFlow 里执行的是遍历任务 traverseArtifactOnly、traverseAndroidJarOnly的话 传入的 FileHandler 为 ClassFileAnalyzer 那么遍历任务会执行 ClassFileAnalyzer里的 handle方法处理
// case 2、MainTransformFlow 里执行的是transform 任务: 传入的FileHandler 为 ClassFileTransformer 并且 如果 fileData 是.class 文件 ,则调用 ClassFileTransformer 的 handle 方法进行处理。
handler.handle(fileData);
}
// 2、非 类 使用 FileProcessor 列表 最后一个 BackupFileProcessor 的 proceed 则 结束链式调用
return chain.proceed(input);
}
遍历任务看ClassFileAnalyzer的 handle方法
@Override
public void handle(FileData fileData) {
try {
...
...
...
byte[] raw = fileData.getBytes();
String relativePath = fileData.getRelativePath();
// 构建 ClassReader 对象,解析文件
ClassReader cr = new ClassReader(raw);
int flag = getFlag(handlers);
// 创建 ClassVisitorChain 对象,本质是 ClassWriter
ClassVisitorChain chain = getClassVisitorChain(relativePath);
// 如果类图构建器为空,则创建 GenerateGraphClassVisitor 类读取类文件,并构建类图
if (this.mGraphBuilder != null) {
// GenerateGraphClassVisitor 根据ClassReader 传入的构建产物.class 然后构建class实体 添加到mGraphBuilder 里 生成类图
chain.connect(new GenerateGraphClassVisitor(process == TRAVERSE_ANDROID, mGraphBuilder));
}
// 根据不同的状态,回调 Plugin 对应的处理过程
pluginList.forEach(plugin -> {
switch (process) {
case TRAVERSE_INCREMENTAL:
plugin.traverseIncremental(fileData, chain);
break;
case TRAVERSE:
plugin.traverse(relativePath, chain);
break;
case TRAVERSE_ANDROID:
// 遍历 Android.jar
plugin.traverseAndroidJar(relativePath, chain);
break;
default:
throw new RuntimeException("Unsupported Process");
}
});
ClassNode cn = new SafeClassNode();
chain.append(cn);
chain.accept(cr, flag);
...
...
...
} catch (Exception e) {
...
}
}
根据ASM GenerateGraphClassVisitor 对类进行访问 创建对应类的节点
public class GenerateGraphClassVisitor extends BaseClassVisitor {
/**
* 根据构建产物 生成的 class 实体 用于构建类图
*/
private ClassEntity entity;
/**
* 是否来源于 Android SDK
*/
private boolean fromAndroidSDK;
/**
* 类图构造者
*/
private GraphBuilder mGraphBuilder;
private AtomicInteger count = new AtomicInteger();
public GenerateGraphClassVisitor(boolean fromAndroidSDK, @Nonnull GraphBuilder graphBuilder) {
this.fromAndroidSDK = fromAndroidSDK;
this.mGraphBuilder = graphBuilder;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
// 创建 class 实体
entity = new ClassEntity(access, name, superName, interfaces == null ? Collections.emptyList() : Arrays.asList(interfaces));
entity.fromAndroid = fromAndroidSDK;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
if(name.equals("wrappedEntity")){
Log.e("flag","-----:"+name+"---:"+entity.name);
}
entity.fields.add(new FieldEntity(access, entity.name, name, desc, signature));
return super.visitField(access, name, desc, signature, value);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
entity.methods.add(new MethodEntity(access, entity.name, name, desc, exceptions));
return super.visitMethod(access, name, desc, signature, exceptions);
}
/**
* 访问完后 把每个元素 添加到 类图构建器里
*/
@Override
public void visitEnd() {
super.visitEnd();
mGraphBuilder.add(entity);
Log.e("tag","-----count"+count.incrementAndGet()+"---:"+entity.name);
}
}
最终添加到mGraphBuilder类图构建器里,
public void add(ClassEntity entity, boolean fromCache) {
// 1、查找元素
final Node current = getOrPutEmpty((entity.access & Opcodes.ACC_INTERFACE) != 0, entity.name);
// 2、检察器: 没检察过 就进入 并且将标记 设为 true
if (!current.defined.compareAndSet(false, true)) {
if (fromCache) {
//先正式添加后面再添加cache,防止cache覆盖了新的数据,此处return
return;
}
if (!entity.fromAndroid && !isCacheValid()) {
String msg = String.format("We found duplicate %s class files in the project.", current.entity.name);
if (BooleanProperty.ENABLE_DUPLICATE_CLASS_CHECK.value() && !"module-info".equals(current.entity.name)) {
throw new DuplicateClassException(msg);
} else {
LevelLog.sDefaultLogger.e(msg);
}
}
}
// 3、记录父类节点,,并将 子类 添加到父类的 子类节点集合里
ClassNode superNode = null;
List<InterfaceNode> interfaceNodes = Collections.emptyList();
// 父类 不为空
if (entity.superName != null) {
// 获取父类节点
Node node = getOrPutEmpty(false, entity.superName);
if (node instanceof ClassNode) {
superNode = (ClassNode) node;
// all interfaces extends java.lang.Object
// make java.lang.Object subclasses purely
// 当前也是类节点, 则将当前类 添加到 父类节点的子节点集合里
if (current instanceof ClassNode) {
synchronized (superNode) {
if (superNode.children == Collections.EMPTY_LIST) {
superNode.children = new LinkedList<>();
}
superNode.children.add((ClassNode) current);
}
}
} else {
throw new RuntimeException(String.format("%s is not a class. Maybe there are duplicate class files in the project.", entity.superName));
}
}
// 4、判断 当前 类实体 所实现的接口 不为空 作用处理接口
if (entity.interfaces.size() > 0) {
interfaceNodes = entity.interfaces.stream()
.map(i -> {
Node node = getOrPutEmpty(true, i);
if (node instanceof InterfaceNode) {
final InterfaceNode interfaceNode = (InterfaceNode) node;
synchronized (interfaceNode) {
// 添加到子接口集合里
if (current instanceof InterfaceNode) {
if (interfaceNode.children == Collections.EMPTY_LIST) {
interfaceNode.children = new LinkedList<>();
}
interfaceNode.children.add((InterfaceNode) current);
} else if (current instanceof ClassNode) {
// 添加到实现类 节点集合里
if (interfaceNode.implementedClasses == Collections.EMPTY_LIST) {
interfaceNode.implementedClasses = new LinkedList<>();
}
interfaceNode.implementedClasses.add((ClassNode) current);
}
}
return (InterfaceNode) node;
} else {
throw new RuntimeException(String.format("%s is not a interface. Maybe there are duplicate class files in the project.", i));
}
})
.collect(Collectors.toList());
}
current.entity = entity;
current.parent = superNode;
current.interfaces = interfaceNodes;
}
设置当前访问类的实体,和父类节点和它所实现的所有接口节点
产物全部构建完,就生成了完整的工程类图
5-4: 公共 MainTransformFlow.runTransform()再次遍历产物,将class传给子插件的transform处理验证合法
MainTransformFlow.java
runTransform() 中的第三过程
// getProcessors : 类别是 Process.TRANSFORM 传入 ClassFileTransformer 返回的是ClassFileProcessor
// 则 transform 传入的是 ClassFileProcessor
transform(getProcessors(Process.TRANSFORM, new ClassFileTransformer(context, new ArrayList<>(handlers), needPreVerify(), needVerify())));
AbsTransformFlow.java
protected AbsTransformFlow transform(FileProcessor... processors) throws IOException, InterruptedException {
markRunningState(TransformContext.State.BEFORETRANSFORM);
beforeTransform(transformEngine);
markRunningState(TransformContext.State.TRANSFORMING);
transform(transformEngine, this.nextTransformFlow == null, processors);
markRunningState(TransformContext.State.AFTERTRANSFORM);
afterTransform(transformEngine);
return this;
}
protected AbsTransformFlow transform(TransformEngine transformEngine, boolean isLast, FileProcessor... processors) throws IOException {
transformEngine.transform(isLast, processors);
return this;
}
transformEngine.transform:
public void transform(boolean isLast, FileProcessor... processors) {
Schedulers.FORKJOINPOOL().invoke(new PerformTransformTask(context.allFiles(), getProcessorList(processors), isLast, context));
}
PerformTransformTask.compute()->FileTraverseTask.compute()->TraverseTask.compute()->ProcessorChain.proceed()
@Override
public Output proceed(Input input) throws IOException {
if (index >= processors.size()) throw new AssertionError();
// 1 index = 0 时 会从 processors 处理器列表中获取第一个处理器—FilterFileProcessor,并调用它的 process 方法
// 2 index = 1 -> processors.size()-1 之前 时 是 ClassFileProcessor 则会执行 ClassFileProcessor.process()
// 3 index = processors.size()-1 时 为 BackupFileProcessor 则执行 BackupFileProcessor.process()
FileProcessor next = processors.get(index);
return next.process(new ProcessorChain(processors, input, index + 1));
}
看 中间做事的ClassFileProcessor.process()
@Override
public Output process(Chain chain) throws IOException {
Input input = chain.input();
FileData fileData = input.getFileData();
if (fileData.getRelativePath().endsWith(".class")) {
// case 1、MainTransformFlow 里执行的是遍历任务 traverseArtifactOnly、traverseAndroidJarOnly的话 传入的 FileHandler 为 ClassFileAnalyzer 那么遍历任务会执行 ClassFileAnalyzer里的 handle方法处理
// case 2、MainTransformFlow 里执行的是transform 任务: 传入的FileHandler 为 ClassFileTransformer 并且 如果 fileData 是.class 文件 ,则调用 ClassFileTransformer 的 handle 方法进行处理。
handler.handle(fileData);
}
// 2、非 类 使用 FileProcessor 列表 最后一个 BackupFileProcessor 的 proceed 则 结束链式调用
return chain.proceed(input);
}
而 MainTransformFlow.runTransform()执行的transform 传入的FileHandler是ClassFileTransformer,则handler.handle(fileData) 为执行ClassFileTransformer的handle方法
关键代码:遍历子插件 将class路径 和 ClassVisitorChain 传给子插件的transform方法
for (MainProcessHandler handler : handlers) {
// 3、遍历执行所有 plugin 的 transform。其内部会使用 chain.connect(new ReferCheckClassVisitor(context)) 的方式
// 比如 ReferCheckPlugin 里的 transform 方法
if (!handler.transform(relativePath, chain)) {
fileData.delete();
return;
}
}
5-5 子插件ReferCheckPlugin的transform方法中结合第一次遍历的生成类图,与 ASM ClassVisitor 进行验证
ReferCheckClassVisitor 进行类访问,ClassVisitor 中 方法是按顺序执行的先执行visit
/**
* 第一个被执行
* des:访问类的头部
* @param version 指类创建时使用的 JDK 的版本,比如 50 代表 JDK1.6、51 代表 JDK1.7
* @param access 代表类的访问权限,比如 public 、private
* @param name 表示类名
* @param signature 表示类的签名,如果类不是泛型或者没有继承泛型类,那么signature 值为空
* @param superName 表示父类的名称
* @param interfaces 表示实现的接口
*/
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
首先检测类或者接口的是否正确且可访问
// 当前访问class的父类 name
if (superName != null) {
// 根据父类名 查找节点
Node superNode = graph.get(superName);
if (superNode == null) {
// 节点没找到的话 报 TYPE_CLASS_NOT_FOUND 类没找到错误
checkIssueReceiver.addNotAccessMember(
className, "<clinit>", "()V", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, sourceFile, -1,
superName, "<clinit>", "()V", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, InaccessibleNode.TYPE_CLASS_NOT_FOUND);
} else if (superNode instanceof InterfaceNode) {
// 如果父类 是 接口节点 报 TYPE_CLASS2INTERFACE 需要类 单 找到了接口
checkIssueReceiver.addNotAccessMember(
className, "<clinit>", "()V", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, sourceFile, -1,
superName, "<clinit>", "()V", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, InaccessibleNode.TYPE_CLASS2INTERFACE);
} else if (TypeUtil.isFinal(superNode.entity.access)) {
// 父类是final 修饰的 报 TYPE_OVERRIDE_FINAL : 继承了 final 修饰的类
checkIssueReceiver.addNotAccessMember(
className, "<clinit>", "()V", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, sourceFile, -1,
superName, "<clinit>", "()V", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, InaccessibleNode.TYPE_OVERRIDE_FINAL);
} else {
// 不报错 就 赋值
this.superNode = (ClassNode) superNode;
}
}
验证当前类实现的接口的可访问性
// 看该类实现了哪些 接口
if (interfaces != null) {
for (String itf : interfaces) {
// 获取父接口 从类图中,如果没找到 报 TYPE_CLASS_NOT_FOUND 类没找到
Node superItf = graph.get(itf);
if (superItf == null) {
checkIssueReceiver.addNotAccessMember(
className, "<clinit>", "()V", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, sourceFile, -1,
itf, "<clinit>", "()V", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, InaccessibleNode.TYPE_CLASS_NOT_FOUND);
} else if (superItf instanceof ClassNode) {
// 如果找到的是 类节点: 报 TYPE_INTERFACE2CLASS 需要接口 但是找到了类
checkIssueReceiver.addNotAccessMember(
className, "<clinit>", "()V", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, sourceFile, -1,
itf, "<clinit>", "()V", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, InaccessibleNode.TYPE_INTERFACE2CLASS);
}
}
}
ReferCheckClassVisitor 然后执行 visitMethod方法, 对方法进行访问
首先检测当前访问类重写的方法Override:通过上一步验证父类合法性,通过类图拿到父类节点,遍历父类的所有方法
/**
* 检查重写的方法 : 重写后 子类 重写的方法 访问权限 不能低于 父类方法的访问权限
* @param node 父类节点
* @param access 方法 的访问权限,比如 public 、private
* @param methodName 方法名字
* @param desc 方法的描述
*/
private void checkOverride(ClassNode node, int access, String methodName, String desc) {
if (node == null) {
return;
}
// 遍历父类的 所有方法
for (MethodEntity method : node.entity.methods) {
// 当前的方法名和 描述 有和 父类的方法名 与描述 相同的则 进入 判断
if (methodName.equals(method.name()) && desc.equals(method.desc())) {
// 父类方法 非静态 && 当前方法非静态
boolean overrideSuccess = !TypeUtil.isStatic(method.access()) && !TypeUtil.isStatic(access);
// 1 父类 方法访问权限 public
if (TypeUtil.isPublic(method.access())) {
// 1-1 (父类public且非static 且 子类非public) 1-2 父类静态和 子类静态不一致 报: TYPE_OVERRIDE_INACCESS 无法访问重写的方法
if ((!TypeUtil.isStatic(method.access()) && !TypeUtil.isPublic(access)) || (TypeUtil.isStatic(method.access()) != TypeUtil.isStatic(access))) {
checkIssueReceiver.addNotAccessMember(
className, methodName, desc, access, sourceFile, -1,
node.entity.name, methodName, desc, method.access(), InaccessibleNode.TYPE_OVERRIDE_INACCESS);
overrideSuccess = false;
}
} else if (TypeUtil.isProtected(method.access())) {
// 2-1 (父类方法 protected 且 非 static 且 子方法非public 非 protected) 2-2 方法 静态不统一 报:TYPE_OVERRIDE_INACCESS 无法访问重写的方法
if ((!TypeUtil.isStatic(method.access()) && !TypeUtil.isPublic(access) && !TypeUtil.isProtected(access)) || (TypeUtil.isStatic(method.access()) != TypeUtil.isStatic(access))) {
checkIssueReceiver.addNotAccessMember(
className, methodName, desc, access, sourceFile, -1,
node.entity.name, methodName, desc, method.access(), InaccessibleNode.TYPE_OVERRIDE_INACCESS);
overrideSuccess = false;
}
} else if (TypeUtil.isPrivate(method.access())) {
// 3 父类 private 不构成复写
overrideSuccess = false;
} else {
//default 包名一样
if (Utils.getPackage(className).equals(Utils.getPackage(node.entity.name))) {
//构成复写 4-1 【父类非 静态 子类 为private 】 4-2 静态不一致 报:TYPE_OVERRIDE_INACCESS 无法访问重写的方法
if ((!TypeUtil.isStatic(method.access()) && TypeUtil.isPrivate(access)) || (TypeUtil.isStatic(method.access()) != TypeUtil.isStatic(access))) {
checkIssueReceiver.addNotAccessMember(
className, methodName, desc, access, sourceFile, -1,
node.entity.name, methodName, desc, method.access(), InaccessibleNode.TYPE_OVERRIDE_INACCESS);
overrideSuccess = false;
}
}
}
// 5 严格检查 无法访问重写的方法
if (checkInaccessOverrideMethodStrictly) {
// 1 子类方法private 2 子类 方法静态 3 父类方法 private 4 父类静态 5 父类public 子类非 public 6 包名不匹配 TYPE_OVERRIDE_INACCESS_STRICT 严格无法访问重写的方法
if (TypeUtil.isPrivate(access) || TypeUtil.isStatic(access) ||
TypeUtil.isPrivate(method.access()) || TypeUtil.isStatic(method.access()) ||
(TypeUtil.isPublic(method.access()) && !TypeUtil.isPublic(access)) ||
(!TypeUtil.isPublic(method.access()) && !TypeUtil.isProtected(method.access()) && !Utils.getPackage(className).equals(Utils.getPackage(node.entity.name)))) {
checkIssueReceiver.addNotAccessMember(
className, methodName, desc, access, sourceFile, -1,
node.entity.name, methodName, desc, method.access(), InaccessibleNode.TYPE_OVERRIDE_INACCESS_STRICT);
}
}
// 检查 重写成功了
if (overrideSuccess) {
// 6 父类非静态 父类 final 方法 报 TYPE_OVERRIDE_FINAL 重写了final 方法
if (!TypeUtil.isStatic(method.access()) && TypeUtil.isFinal(method.access())) {
checkIssueReceiver.addNotAccessMember(
className, methodName, desc, access, sourceFile, -1,
node.entity.name, methodName, desc, method.access(), InaccessibleNode.TYPE_OVERRIDE_FINAL);
}
return;
}
}
}
// 父类的parent 递归验证
checkOverride(node.parent, access, methodName, desc);
}
然后判断当前访问的方法是非原生方法,则调用ASM的 MethodVisitor 对方法 和 字段进行访问
首先
/**
* 访问方法时的指令,即调用方法的指令,比如在一个方法中调用另一个方法;
*
* @param opcode 要访问的类型指令的操作码。此操作码可以是INVOKEVIRTUAL、INVOKESPECIAL、INVOKESTATIC或INVOKEINTERFACE。
* @param owner 方法的所有者类的名字
* @param name 方法名字
* @param desc 方法描述
* @param itf 是否方法的所有者类是接口。
*/
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
...
...
// 验证方法
checkMethod(opcode, owner, name, desc, itf);
...
...
}
// 根据类图获取方法所在的类节点
Node ownerNode = graph.get(owner);
ownerNode == null -> InaccessibleNode.TYPE_CLASS_NOT_FOUND 报类找不到的错误
类不为null的话,判断 方法
// 找到 类图里原始的方法实体,就是看从哪里继承的方法、或者自有方法
MethodEntity originMethod = ownerNode.confirmOriginMethod(name, desc);
originMethod == null -> InaccessibleNode.TYPE_METHOD_NOT_FOUND 找不到方法
循环遍历父类 或者 接口去找 当前类名 和 包名的 方法
public MethodEntity confirmOriginMethod(String name, String desc) {
Queue<Node> interfaces = new LinkedList<>();
Node node = this;
while (node != null) {
Optional<MethodEntity> findMethod = node.entity.methods.stream()
.filter(m -> name.equals(m.name()) && desc.equals(m.desc()))
.findAny();
if (findMethod.isPresent()) {
MethodEntity realMethod = findMethod.get();
String owner = node.entity.name;
if (TypeUtil.isPrivate(realMethod.access) && !owner.equals(realMethod.className)) {
return null;
}
return realMethod;
}
node.interfaces.forEach(interfaces::offer);
node = node.parent;
}
MethodEntity candidate = null;
while (!interfaces.isEmpty()) {
Node interfaceNode = interfaces.poll();
if (interfaceNode == null) continue;
Optional<MethodEntity> findMethod = interfaceNode.entity.methods.stream()
.filter(m -> name.equals(m.name()) && desc.equals(m.desc()))
.findAny();
if (findMethod.isPresent()) {
MethodEntity realMethod = findMethod.get();
if (TypeUtil.isAbstract(realMethod.access)) {
candidate = realMethod;
continue;
}
return realMethod;
}
if (interfaceNode.interfaces != null) {
interfaceNode.interfaces.forEach(interfaces::offer);
}
}
return candidate;
}
拿到originMethod 不为null 且 为抽象方法:
if (TypeUtil.isAbstract(originMethod.access())) {
// Add all children to checklist
// 当前方法来自于接口
if (itf) {
// 如果方法所在的类不是接口 则报错
if (!(ownerNode instanceof InterfaceNode)) {
throw new RuntimeException(String.format("%s should be a interface, but it's a class now. It was referred at class [%s], method [%s].", ownerNode.entity.name, this.className, this.methodName));
}
// 遍历 方法所在类的子类
graph.traverseChildren((InterfaceNode) ownerNode, child -> {
// 子类 是非抽象的子类
if (child instanceof ClassNode && !TypeUtil.isAbstract(child.entity.access)
&& TypeUtil.isAbstract(child.confirmOriginMethod(name, desc).access())) {
//1-1 原始方法是抽象方法 1-2当前方法来源于接口 1-3 方法所在接口 的非抽象子类 对应的方法是抽象方法
// 【不可能存在这种情况,所有就是没实现那个方法】
//错误:被调用方法的类名 是这个非抽象子类 没有实现这个抽象类 报错:TYPE_NOT_IMPLEMENT
checkIssueReceiver.addNotAccessMember(
className, methodName, methodDesc, methodAccess, sourceFile, processingLineNumber,
child.entity.name, name, desc, originMethod.access(), InaccessibleNode.TYPE_NOT_IMPLEMENT);
}
return false;
});
} else {
// 来源于抽象类
if (!(ownerNode instanceof ClassNode)) { // 不是类节点 报错
throw new RuntimeException(String.format("%s should be a class, but it's an interface now. It was referred at class [%s], method [%s].", ownerNode.entity.name, this.className, this.methodName));
}
// 前提: 2-1 源方法为抽象方法 2-2 当前方法所在 为类节点 2-3 非抽象子类 拥有这个抽象方法 【不可能】 故报错 TYPE_NOT_IMPLEMENT没有实现相关方法
// 遍历子类
graph.traverseChildren((ClassNode) ownerNode, child -> {
if (!TypeUtil.isAbstract(child.entity.access) && TypeUtil.isAbstract(child.confirmOriginMethod(name, desc).access())) {
// 非抽象子类没有实现这个抽象方法
checkIssueReceiver.addNotAccessMember(
className, methodName, methodDesc, methodAccess, sourceFile, processingLineNumber,
child.entity.name, name, desc, originMethod.access(), InaccessibleNode.TYPE_NOT_IMPLEMENT);
}
return false;
});
}
}
非抽象方法 ,验证可访问性:
// 传入 1 操作方法的指令 2 源方法
if (!accessible(opcode, originMethod)) {
checkIssueReceiver.addNotAccessMember(
className, methodName, methodDesc, methodAccess, sourceFile, processingLineNumber,
owner, name, desc, originMethod.access(), InaccessibleNode.TYPE_INACCESS);
}
/**
* 验证是否能访问 指定的方法/字段
* @param opcode 要访问的类型指令的操作码。此操作码可以是
* INVOKEVIRTUAL[调用实例方法]、
* INVOKESPECIAL【调用构造器方法,private方法,或者超类方法】、
* INVOKESTATIC【调用静态方法】
* INVOKEINTERFACE【调用接口方法】。
* @param originMember 原始的方法实体
* @return
*/
private boolean accessible(int opcode, MemberEntity originMember) {
// 是否是静态方法
boolean isStaticMember = TypeUtil.isStatic(originMember.access());
// 1 opcode 指令是 要调用静态方法 且 当前方法也是静态方法
// 2 GETSTATIC 设置 / 获取静态字段 且 字段的也是静态的
if ((opcode == Opcodes.INVOKESTATIC) == isStaticMember ||
(opcode == Opcodes.GETSTATIC || opcode == Opcodes.PUTSTATIC) == isStaticMember) {
return accessible(originMember);
}
return false;
}
/**
* 验证是否能访问指定的方法
* @param member
* @return
*/
private boolean accessible(MemberEntity member) {
// 当前类是 方法/字段 所在的类
if (className.equals(member.className())) {
return true;
}
// 方法/字段 是public
if (TypeUtil.isPublic(member.access())) {
return true;
} else if (TypeUtil.isProtected(member.access())) {
//同包名或者继承关系 At same package or inheritance relationship
return Utils.getPackage(className).equals(Utils.getPackage(member.className())) ||
graph.get(this.className).inheritFrom(graph.get(member.className()));
} else if (TypeUtil.isPrivate(member.access())) {
// 私有方法/字段 就无法访问
return false;
} else {
//同包名
return Utils.getPackage(className).equals(Utils.getPackage(member.className()));
}
}
visitFieldInsn方法来验证 字段的合法性
/**
* 访问一个字段时的指令,即加载一个字段(load)或保存一个字段值(store)
* @param opcode 要访问的类型指令的操作码。此操作码为GETSTATIC、PUTSTATIC、GETFIELD或PUTFIELD。 即访问的字段只能是实例字段或静态字段
* @param owner 字段所有者类的内部名称
* @param name 字段名
* @param desc 字段的描述
*/
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
checkField(opcode, name, desc, owner);
super.visitFieldInsn(opcode, owner, name, desc);
}
checkField 方法中
首先获取字段所在的类节点:
Node ownerNode = graph.get(owner);
ownerNode == null -> InaccessibleNode.TYPE_CLASS_NOT_FOUND
然后 获取 字段原始定义的地方?
FieldEntity originField = ownerNode.confirmOriginField(name, desc);
originField == null -> InaccessibleNode.TYPE_FIELD_NOT_FOUND 字段不存在
最后验证字段是否可访问
// 能找到字段 看能不能访问 不能访问 TYPE_INACCESS
if (!accessible(opcode, originField)) {
checkIssueReceiver.addNotAccessMember(
className, methodName, methodDesc, methodAccess, sourceFile, processingLineNumber,
owner, name, desc, originField.access(), InaccessibleNode.TYPE_INACCESS);
}
六、bytex-referCheck原理总结
主要三个过程
- 将检测子插件ReferCheckPlugin与bytex宿主插件关联
- 在宿主插件的transform中遍历子插件的transform【默认为全局的MainTransformFlow】对遍历产物处理,借助ASM API对类,接口,方法,字段等进行访问,构建相应实体进而构建完整类图
- 再次遍历构建产物,将class交给子插件ReferCheckPlugin 借助ASM API 进行类/接口,方法,字段访问,借助完整工程的类结构图,进行合法性验证