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

分析OC代码

搭建App项目,打开ViewController字符是什么.m文件,写入以下代码:

#import "ViewController.h"
@interface ViewController ()
@property(nonatomic, strong) NSString* name;
@property(nonatomic, strong) NSArray* arrs;
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
}
@end

生成AST代码,找到属性的声明

iOS之LLVM 七

  • ObjCPropertyDecl节点中,可以找到属APP性的声明,包含属性的类型和修饰符
AST节点的过滤

系统API提供MatchFinder,用于AST语法树节点的查找

其中addMatcher函数,可以查找指定节点

void addMatcher(const DeclarationMatcher &NodeMatch,
                  MatchCallback *Action);
  • 参数1:设置指定节点
  • 参数2:执行xcode是什么软件字符调,此处并非使用回调函数,而是一个回调类。需要继承MatchCallback系统类,实现自己的子测试用例

添加MatchFinder所在命名空间

using namespace clang::ast_matchers;

实现HKMatchHandler回调类,继承自MatchCallback

class HKMatchHandler:public MatchFinder::MatchCallback{
    public:
        void run(const MatchFinder::MatchResult &Result) {
            const ObjCPropertyDecl *propertyDecl = Result.Nodes.getNodeAs<ObjCPropertyDecl>("objcPropertyDecl");
            if(propertyDecl){
                string typeStr = propertyDecl->getType().getAsString();
                cout<<"------拿到了:"<<typeStr<<endl;
            }
        }
};
  • 必须实现run函数,它就是真正的回调函数
  • xcode是什么软件Result结果,获取节点对象
  • 通过节点对象的getType().getAsString(),以字符串的形式返回属性类型

HKxcode是什么意思Cons测试用例umer类中,定义私有MatchFinderH测试仪KMatchHandler,重写构造方法,添加AST节点过滤器

class HKConsumer:public ASTConsumer{
    private:
        MatchFinder matcher;
        HKMatchHandler handler;
    public:
        HKConsumer(){
            matcher.addMatcher(objcPropertyDecl().bind("objcPropertyDecl"), &handler);
        }
};
  • 解析语法树,查找objcPropertyDecl节点

在文件解析完成的回调函数中,调用matchermatchAST函数,将文件的语法树传入过滤器

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

测试插件

iOS之LLVM 七

  • 通过语法树分析,可以找到属性的声明,包含属性的类型和修饰符
  • 但也存在一些问题,在预处理阶段,头文件会被展开,测试抑郁程度的问卷我们可能会获Xcode取到系统头文件中的属性,所以我们要想办法过滤掉系统文件中的代码
过滤系统文件

可以通过文件路径判断系统文件,因为系统文件都存在于/Applicappointmentations/Xcode.app/开头的目录中

PluginASTAction类中,存在CompilerInstance类型的CI参数

std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
                                                 StringRef InFile) override = 0;
  • CI为编译器实例对象,可以通过它获取到文件路径,以及警告的提示

重写HKConsumer的构造函数,增加CI参数

HKConsumer(CompilerInstance &CI){
    matcher.addMatcher(objcPropertyDecl().bind("objcPropertyDecl"), &handler);
}

HKASxcode修改项目名TAction类中,创建ASTConsumer时,将CI传入

std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
    return unique_ptr<HKConsumer> (new HKConsumer(CI));
}

重写HKMatchHandlerxcode汽车报价构造函数,增加CI参数。定义私有CompilerInstance,通过构造函数对其赋值

class HKMatchHandler:public MatchFinder::MatchCallback{
    private:
        CompilerInstance &CI;
    public:
        HKMatchHandler(CompilerInstance &CI):CI(CI){
        }
};

HKConsumer的构造函数中,对HKMatchHandler中的C字符串逆序输出I进行传递

HKConsumer(CompilerInstance &CI):handler(CI){
    matcher.addMatcher(objcPropertyDecl().bind("objcPropertyDecl"), &handler);
}

在HKMatchHaxcode是什么软件ndxcode是什么软件ler使用CI,获取文件路径并进行过滤

class HKMatchHandler:public MatchFinder::MatchCallback{
    private:
        CompilerInstance &CI;
        bool isUserSourceCode(const string fileName){
            if(fileName.empty()){
                return false;
            }
            if(fileName.find("/Applications/Xcode.app/")==0){
                return false;
            }
            return true;
        }
    public:
        HKMatchHandler(CompilerInstance &CI):CI(CI){
        }
        void run(const MatchFinder::MatchResult &Result) {
            const ObjCPropertyDecl *propertyDecl = Result.Nodes.getNodeAs<ObjCPropertyDecl>("objcPropertyDecl");
            string fileName =  CI.getSourceManager().getFilename(propertyDecl->getSourceRange().getBegin()).str();
            if(propertyDecl && isUserSourceCode(fileName)){
                string typeStr = propertyDecl->getType().getAsString();
                cout<<"------拿到了:"<<typeStr<<endl;
            }
        }
};
  • 通过CI.getSourceManager().getFilename获取文件名称,包含文件路径
  • 需要传入SourceLocation,可以通过节点的propertyDecl测试你的自卑程度->getSourceRange().getBegin()获得
  • 实现isUserSourceCode函数,判断路径非空,并且非/Applications/Xcode.app/目录开头,视为自定义文件

测试插件

文件解析完成...
------拿到了:NSString *
------拿到了:NSArray *
  • 成功过滤系统文件,获取到自定义文件中的两个属性