一、热补丁的本质

对于线上紧急的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", );
// 为类NewClass添加实例变量newIvar,类型为NSString
class_addIvar(NewClass, "newIvar", sizeof(NSString *), , "@");

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

获取类名

获取方法名

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

YOCVM的更多相关文章

  1. grep之字符串搜索算法Boyer-Moore由浅入深(比KMP快3-5倍)

    这篇长文历时近两天终于完成了,前两天帮网站翻译一篇文章“为什么GNU grep如此之快?”,里面提及到grep速度快的一个重要原因是使用了Boyer-Moore算法作为字符串搜索算法,兴趣之下就想了解 ...

随机推荐

  1. php中mysql和mysqli的总结

    首先php—mysql 是 php 操作 mysql 资料库最原始的的拓展 而php—mysqli,字母i代表的 Improvement ,提更了相对进阶的功能. 推荐学习和使用mysqli mysq ...

  2. 微信小程序开发BUG经验总结

    摘要: 常见的微信小程序BUG! 小程序开发越来越热,开发中遇到各种各样的bug,在此总结了一些比较容易掉进去的坑分享给大家. 1. new Date跨平台兼容性问题 在Andriod使用new Da ...

  3. Linux 为linux enterprises 6安装图形桌面教程

    为linux enterprises 6安装图形桌面教程 by:授客 QQ:1033553122 安装系统后发现没图形界面,安装Xwindow[为了避免权限不足,以root登录] 步骤1.启动图形界面 ...

  4. 安卓开发_浅谈ListView之分页列表

    前言: 在开发的过程中,有时候我们需要从网络解析一些数据,比如最近的一些新闻,我们需要把这些数据用ListView显示出来. 因为是解析一个网络数据源,这样将会一下子将所有的数据解析出来,当数据源数据 ...

  5. WANem广域网环境模拟

    背景 在测试过程中,往往需要模拟网络环境较差情况下,体验情况,故引入广域网模拟工具辅助测试 安装WANem 步骤1:光盘引导WANem镜像: 步骤2:启动(或者新建虚拟机——选择other insal ...

  6. (网页)alert()怎么回事出不来,代码没有写错

    1.不报错,请查看浏览器是否禁掉了alter. 2.console.log()输出,避免这种尴尬.

  7. 单元测试工具Junit浅谈

    什么是单元测试?   写了一个类和一些方法,给别人用,会不会有bug?那就测一下这些方法吧 怎么测?   用main方法测?不能一起运行,需要人为观察输出是否正确,测试效率低 单元测试能带来什么好处? ...

  8. 关于《SQLSERVER走起》微信账号自动回复功能的升级

    关于SQLSERVER走起微信账号自动回复功能的升级 由于腾讯对微信公众账号的升级,本公众账号也增加了关键词自动回复功能, 只需要输入某些特定关键词,本公众账号就会进行自动回复. 例如: 输入 sql ...

  9. [20180317]12c TABLE ACCESS BY INDEX ROWID BATCHED.txt

    [20180317]12c TABLE ACCESS BY INDEX ROWID BATCHED.txt --//简单探究12c TABLE ACCESS BY INDEX ROWID BATCHE ...

  10. 为什么内核访问用户数据之前,要做access_ok?【转】

    linuxer 案例 比如内核的如下commit引入了一个严重的安全漏洞(编号CVE-2017-5123): 危害 一个攻击案例可以参考: freebuf <Linux内核Waitid系统调用本 ...