zoukankan      html  css  js  c++  java
  • YOCVM

    一、热补丁的本质

        对于线上紧急的bug,重新提审AppStore的时间过长。因此,能够下发一段补丁代码到线上运行,并结合Runtime,实时改变App原有的行为,就显得极为重要。补丁代码的形式可以有很多种,比如文本形式的JS脚本,还有二进制形式的动态库。只要能执行即可,借助系统的原生能力或者外部引入的执行引擎。

    二、尴尬的现状

        1、众多热补丁框架,如JSPatch,被苹果爸爸禁掉。

        2、动态库有签名校验,无法通过下发动态库来改变线上程序运行。      

    三、由Java想到的新套路

        我们知道,Java从源代码到执行,大致经过如图一所示的两个步骤:1)源代码由编译器编译成字节码;2)字节码由JVM解释执行。

    图一

        因此,下发的补丁也可以是字节码,只要自己实现:1)编译器YOCCompiler;2)虚拟机YOCVM。

    图二

    四、编译原理

        我们知道,编译过程大致如图三:

    图三

     

        然而,Objective-C作为一门高级语言,语法规则特别多,想从词法分析开始实现编译器不切实际。所幸的是,Xcode默认的编译器LLVM是开源的,我们可以对其进行二次开发来满足自己的需求。编译器通常分为前端和后端,如图四所示,Clang是LLVM的前端。

     图四

     

        可以看到,Clang已经帮我们做了很多事情,我们可以直接利用语法分析的结果,即抽象语法树(AST)。 

    clang -fmodules -fsyntax-only -Xclang -ast-dump ASTDemo.m

        AST主要有两种节点,Decl(声明)和Stmt(语句)。

    Class MyClass = objc_allocateClassPair([NSObject class], "MyClass", 0);

    Objective-C运行时动态创建类、添加方法、添加实例变量。

    // 创建一个NSObject的子类NewClass
    Class NewClass = objc_allocateClassPair([NSObject class], "NewClass", 0);
    View Code
    // 为类NewClass添加实例变量newIvar,类型为NSString
    class_addIvar(NewClass, "newIvar", sizeof(NSString *), 0, "@");
    View Code

     虚拟机主要有两种,基于寄存器的和基于栈的。

    获取类名

    获取方法名

    template<typename Derived>
    class clang::RecursiveASTVisitor<Derived>;

    1、安装cmake和ninja

    brew install cmake
    
    brew install ninja
    

    表达式树 

    一条指令占一个字节,jvm 

    xcode使用的是默认的clang

    ABI(application binary interface)应用二进制接口

    TraverseStmt

    TraverseDecl

    bool VisitFunctionDecl(FunctionDecl *decl){
        string name = decl->getNameAsString(); // 函数名
        printf(name);
        return true;
    }
    

      

    ASTConsumer

    TranslationUnitDecl

    TypedefDecl

    ImportDecl

    FunctionDecl

    ParmVarDecl

    CompoundStmt

    IfStmt

    BinaryOperator

    继承模板类

    基于栈的虚拟机

    基于寄存器的虚拟机

    重写VisitDecl

    是否是interface声明if(isa<ObjCInterfaceDecl>(decl)){}

    开发clang插件:YOCCompiler

    Plugin没有主函数,它们的入口是向Clang注册的PluginASTAction

    获取父类名称:interfDecl->getSuperClass()->>getNameAsString()

    获取当前类名称:objcClsInterface = interfDecl->getNameAsString();后面获取类的名称都与此类似

    获取实现的所有ObjCProtocolDecl:
    for(ObjCList<ObjCProtocolDecl>::iterator it = interfDecl->all_referenced_protocol_begin();it!=interfDecl->all_referenced_protocol_end();it++){
    (*it)->getNameAsString();
    }

    获取接口文件名称: context->getSourceManager().getFilename(interfDecl->getSourceRange().getBegin()).str();

    https://www.ibm.com/developerworks/cn/opensource/os-cn-clang/index.html

    https://clang.llvm.org/doxygen/classclang_1_1RecursiveASTVisitor.html

    https://blog.csdn.net/taishanduba/article/details/59799717

    http://www.njiang.cn/2017/03/03/%E5%8E%9F%E5%88%9B-%E5%85%B3%E4%BA%8E%E5%A6%82%E4%BD%95%E7%94%A8Xcode%E8%B0%83%E8%AF%95%E5%BC%80%E5%8F%91clang%E6%8F%92%E4%BB%B6/

    2.是否是实现类

    if(isa<ObjCImplDecl>(decl)){}

    3.是否是category

    if(isa<ObjCCategoryDecl>(decl)){}

    4.是否是协议

    if(isa<ObjCProtocolDecl>(decl)){}

    5.是否是property

    if(isa<ObjCPropertyDecl>(decl)){}

    是否是实例变量:objcIsInstanceMethod = propertyDecl->isInstanceProperty();
    property类型(修饰符例如NSString):propertyDecl->getType().getAsString();
    getter方法名称:propertyDecl->getGetterName().getAsString()
    setter方法名称:propertyDecl->getSetterName().getAsString()
    是否只读:propertyDecl->isReadOnly()
    是否是类property:propertyDecl->isClassProperty()
    是否是原子性:propertyDecl->isAtomic()

    6.是否是成员变量

    if (isa<ObjCIvarDecl>(decl)) {}

    成员变量名称:ivarDecl->getNameAsString()
    1
    7.是否是参数
    if (isa<ObjCTypeParamDecl>(decl)){}

    @interface NSDictionary<Key : id<NSCopying>, Value>@end
    key,value就是paramter
    1
    2
    8.是否是方法
    if(isa<ObjCMethodDecl>(decl)){}

    是否是实例方法: methodDecl->isInstanceMethod()
    selector名称: methodDecl->getSelector().getAsString()
    返回值类型:methodDecl->getReturnType().getAsString()
    参数:
    for(ArrayRef<ParmVarDecl *>::iterator it = methodDecl->param_begin();it!=methodDecl->param_end();it++){
    cout<<"参数:"<<((*it)->getNameAsString())<<"参数类型:"<<(*it)->getType().getAsString()<<endl;
    }


    5.重写VisitStmt
    1.是否是变量方法枚举等
    if(isa<DeclRefExpr>(s)){}

    声明的名称:callExpr->getDecl()->->getNameAsString()
    是否是变量:isa<VarDecl>(decl)
    是否是函数:isa<FunctionDecl>(decl)
    是否是枚举:isa<EnumConstantDecl>(decl)


    2.向object-c对象发送消息
    isa<ObjCMessageExpr>(s)
    调用者:objcExpr->getSelector().getAsString()
    函数本身名称: objcExpr->getSelector().getAsString()
    接受消息类型:objcExpr->getReceiverType().getAsString()

    如何调试开发插件

    YOCBridge

    虚拟机会根据字节码里类的相关信息来动态获取类或者创建类,而字节码里类的相关信息就是从AST里遍历得到的

    http://clang.llvm.org/docs/ClangPlugins.html

    https://juejin.im/entry/59f984386fb9a0451c39b682

  • 相关阅读:
    ubuntu14.0 服务器webmin安装
    python如何简单的处理图片(1):打开显示
    如何在YouTube上下载视频
    Ubuntu下下载使用sublime
    IndentationError: unexpected indent
    Linux下vim显示行数
    Linux下tree的使用介绍
    Mac利用分屏spliter
    redis开机自启动脚本(linux)
    java 反射应用
  • 原文地址:https://www.cnblogs.com/yangwenhuan/p/9525847.html
Copyright © 2011-2022 走看看