iOS之LLVM 六

130 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

编写插件代码

文件和顶级节点的解析

导入插件使用的头文件和命名空间

#include <iostream>
#include "clang/AST/AST.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/FrontendPluginRegistry.h"

using namespace clang;
using namespace std;
using namespace llvm;

定义命名空间、定义HKASTAction类,继承自系统的PluginASTAction

namespace HKPlugin {

    class HKASTAction:public PluginASTAction{
        
    };
}

注册插件

static FrontendPluginRegistry::Add<HKPlugin::HKASTAction> X("HKPlugin","this is the description");
  • 参数1:插件名称
  • 参数2:插件描述 现有的需求分为三个步骤:
  • 【第一步】读取代码
  • 【第二步】找到目标类型定义的属性和修饰符
  • 【第三步】不符合标准,提示警告

实现需求的第一步读取代码,需要用到AST语法树,然后对AST节点进行解析

我们可以使用以下两个函数

  • CreateASTConsumer
  • ParseArgsHKASTAction类中,重写CreateASTConsumerParseArgs函数
namespace HKPlugin {
    class HKASTAction:public PluginASTAction{
        public:
            std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
                return unique_ptr<ASTConsumer> (new ASTConsumer);
            }
        
            bool ParseArgs(const CompilerInstance &CI, const std::vector<std::string> &arg) {
                return true;
            }
    };
}

ASTConsumer是系统提供的基类,作为基类,它的作用大多有两种

  • 抽取代码
  • 由开发者继承,实现它的子类,对其进行扩展

所以,我们不能直接使用ASTConsumer,需要对其进行继承,实现自定义子类

namespace HKPlugin {

    class HKConsumer:public ASTConsumer{
        public:
            bool HandleTopLevelDecl(DeclGroupRef D) {
                cout<<"正在解析..."<<endl;
                return true;
            }

            void HandleTranslationUnit(ASTContext &Ctx) {
                cout<<"文件解析完成..."<<endl;
            }
    };

    class HKASTAction:public PluginASTAction{
        
        public:
        
            std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
                return unique_ptr<HKConsumer> (new HKConsumer);
            }
        
            bool ParseArgs(const CompilerInstance &CI, const std::vector<std::string> &arg) {
                return true;
            }
    };
}

重写HandleTopLevelDeclHandleTranslationUnit函数

  • HandleTopLevelDecl:顶级节点解析回调函数,顶级节点,例如:全局变量、函数定义、属性
  • HandleTranslationUnit:整个文件解析完成后的回调

编译HKPlugin项目,在项目的Products目录下,找到编译出的clang可执行文件

image-18.png 同样在Products目录下,找到HKPlugin.dylib

image-19.png

使用插件,测试文件和顶级节点的解析

创建hello.m文件,写入以下代码:

int sum(int a);
int a;

int sum(int a){
    int b = 10;
    return 10 + b;
}

int sum2(int a,int b){
    int c = 10;
    return a + b + c;
}

使用以下命令,测试插件

//自己编译的clang路径 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.5.sdk/ -Xclang -load -Xclang 插件(.dylib)路径 -Xclang -add-plugin -Xclang 插件名称 -c 源码路径

/Volumes/study/Source/llvm-hk/build_xcode/Debug/bin/clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.5.sdk/ -Xclang -load -Xclang /Volumes/study/Source/llvm-hk/build_xcode/Debug/lib/HKPlugin.dylib -Xclang -add-plugin -Xclang HKPlugin -c hello.m

-------------------------

//输出以下内容:
正在解析...
正在解析...
正在解析...
正在解析...
文件解析完成...
  • 共解析出四个顶级节点