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", );
// 为类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
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的更多相关文章
- grep之字符串搜索算法Boyer-Moore由浅入深(比KMP快3-5倍)
这篇长文历时近两天终于完成了,前两天帮网站翻译一篇文章“为什么GNU grep如此之快?”,里面提及到grep速度快的一个重要原因是使用了Boyer-Moore算法作为字符串搜索算法,兴趣之下就想了解 ...
随机推荐
- 撩课-Java每天5道面试题第17天
116.说下Struts的设计模式 MVC模式: web应用程序启动时 就会加载并初始化ActionServler. 用户提交表单时, 一个配置好的ActionForm对象被创建, 并被填入表单相应的 ...
- Java开发中json使用,各对象与json相互转换
Json:一种网络通信使用的数据格式,因为便于解析,比较流行,对象可以转为json,同样json也可以转对象. 下面介绍下Json工具的简单使用(fastjson && jackson ...
- netty 基础
Netty是一个高性能.异步事件驱动的NIO框架,提供了对TCP.UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户 ...
- Android ContentProvider数据共享
一.构造一个自己的Provider实现App之间数据共享 1.我们先来了解一下 Uri(统一资源定位符) 定义:每一个Content Provider使用一个公开的URI唯一标示其数据集,Andr ...
- 去除git版本控制
命令:find . -name ".git" | xargs rm –Rf linux $ find . -type d -iname '__pycache__' -exec rm ...
- Flutter 相机定制
Flutter中与硬件相关的部分,一直都挺蛋疼的.方案基本上有两种,自己写,或者等出相关的库. 最近做的一个项目中,需要对相机做定制.有过相关模块开发经验的,就知道这种需求并不简单,况且是这种跨平台解 ...
- loadrunner录制的时候如何应对验证码的问题解决办法?
对这个问题,我个人的看法是,基本上可以考虑从三个途径来解决该问题: 1.第一种方法,也是最容易想到的,在被测系统中暂时屏蔽验证功能,也就是说,临时修改应用,无论用户输入的是什么验证码,都认为是正确的. ...
- http2
原文转至:https://zhuanlan.zhihu.com/p/26559480 HTTP/2 是 HTTP 协议自 1999 年 HTTP 1.1 发布后的首个更新,主要基于 SPDY 协议.由 ...
- Fedora 29 查看 rpm 包 依赖性 以 libconfig 为例
查看依赖性方法:# rpmrepater会向用户显示已安装包的列表,你可以使用上/下箭头来滚动屏幕# 可以在指定包上使用"r"键来显示其依赖关系,循环在指定包上按下"r& ...
- Python基础知识:while循环
1.在循环中使用continue输出1-10之间的奇数 num=0 while num <10: num += 1 if num %2 == 0: #--%--运算符,相除返回余数 contin ...