bytex-referCheck原理解析

一、背景及收益

升级sdk或更新依赖库后,可能因为库之间依赖的版本号不同,API有变动时会报:NoSuchMethodError 等错误

二、ByteX实现原理

ByteX是一个基于gradle transform api和ASM的字节码而实现的

三、bytex-referCheck 检测插件的整体实现思路:

  1. 将所有的子插件注册到宿主插件中,并给每个子插件绑定一个TransformFlow【默认为全局MainTransformFlow】
  2. 宿主插件的Transform方法中遍历执行每个子插件的TransformFlow执行run方法对.class进行处理:
    首先遍历工程中所有的构建产物(.class)和 遍历android.jar里的所有class文件生成类结构图【map:key(className),value(类/接口node)】;
    然后再遍历一次工程中所有的构建产物,将每个class文件传给bytex-referCheck子插件的transform方法处理
  3. 子插件ReferCheckPlugin的transform方法中结合第一次遍历的生成类图,与 ASM ClassVisitor 进行合法性验证

四、名词解释

  1. traverse过程:遍历一次工程中所有的构建产物(一般来说是class文件),单纯做遍历分析,不对输入文件做修改;
  2. traverseAndroidJar过程:遍历android.jar里的所有class文件 (哪个版本的android.jar由工程中的target api决定),主要是为了形成完整的类图.
  3. transform:再遍历一次工程中所有的构建产物,并对class文件做处理后输出(可能是直接回写到本地,也可能作为下一个处理过程的输入)
  4. 类图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原理总结

主要三个过程

  1. 将检测子插件ReferCheckPlugin与bytex宿主插件关联
  2. 在宿主插件的transform中遍历子插件的transform【默认为全局的MainTransformFlow】对遍历产物处理,借助ASM API对类,接口,方法,字段等进行访问,构建相应实体进而构建完整类图
  3. 再次遍历构建产物,将class交给子插件ReferCheckPlugin 借助ASM API 进行类/接口,方法,字段访问,借助完整工程的类结构图,进行合法性验证
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值