flutter之dartsdkCFE(公共前端),AST(语法)代码分析
Flutter底层代码解析
一,概要:
Flutter为dart语言,engine里主要是在dart语言之上开发出来的一套引擎,类似于我们自己开发的公共接口,组件等,从而方便开发人员.分析源码目前只分析dart语言.需要下载dartsdk
主要分析从dart源码到机器码是如何转换的,比如以下代码:
printMsg()=>print("hello");
int test()=>123;
class Printer {
void print_data() {
print(test());
}
}
class ConsolePrinter implements Printer {
void print_data() {
printMsg();
}
}
void testprint(){
print("hello3");
}
void main(){
print("Hello dart");
ConsolePrinter cp= new ConsolePrinter();
cp.print_data();
}
转换为以下格式(网上可参考)
1,andriod/linux的格式so(ELF)文件:
2,mac的match-o文件:
之后就是交给系统运行处理,总之流程简化为一种文件格式到另一种文件格式,即源码文件,经过编译器处理变成系统可执行的文件.
二,从源码到AST语法
1,既然是文件格式到另一种文件格式的转换,转换过程基本都有一套统一的流程.即流程如下:
除了汇编语言,其他可分为token,ast,cfg,IL语言,编译优化(SSA,内联缓存,OSR)等七大块.
2,token流(在源码的scanner目录下):
找到sdk/pkg/_fe_analyzer_shared/lib/src/scanner/token.dart以及
sdk/runtime/vm/token.h
包含的词法有操作符,运算符,关键字,标识符,分隔符,注释,字面量等
static const TokenType FUNCTION = const TokenType(
/* index = */ 31, "=>", "FUNCTION", NO_PRECEDENCE, FUNCTION_TOKEN);
运算符:
static const TokenType GT = const TokenType(
/* index = */ 32, ">", "GT", RELATIONAL_PRECEDENCE, GT_TOKEN,
isOperator: true, isBinaryOperator: true, isUserDefinableOperator: true);
操作符:
static const TokenType INDEX_EQ = const TokenType(
/* index = */ 40, "[]=", "INDEX_EQ", NO_PRECEDENCE, INDEX_EQ_TOKEN,
isOperator: true, isUserDefinableOperator: true);
关键字:
static const Keyword BREAK =
const Keyword(/* index = */ 85, "break", "BREAK", KeywordStyle.reserved);
index为优先级
3,token结构:
结论:token流为双向链表,通过scanner一行一行扫描,获取token流,且每个字符占多少字节.
4,ast语法(在源码的parser目录下)
结构图:
1,语法树(Abstract Syntax Tree, AST)是一种表示程序语法结构的树形数据结构。它是从语法分析得到的,通常由词法分析器和语法分析器生成。语法树把程序语法中的各个组成部分转换为树上的节点。每个节点都代表了语法的一个部分,例如:函数声明(FunctionDeclaration),类声明(ClassDeclaration),运算符,表达式(Express)等。
2,AST由排列成树的节点("AstNode"的实例)组成。也就是说,每个节点可以有零个或多个子节点,每个子节点也是一个节点,而根节点以外的每个节点都只有一个父节点。定义了AST模型。
3,节点组织在树结构中,其中节点的子节点是节点的更小的语法单元。例如,二进制表达式由两个子表达式(操作数)和一个操作符组成。两个表达式表示为节点。操作符不表示为节点。
该AST由基于扫描器生成的token序列的解析器构建。大多数节点提供对构建节点所使用的token的直接访问。例如,可以从表示二进制表达式的节点访问操作符的token。
虽然任何节点理论上可以是AST结构的根,但分析师已知的几乎所有AST结构都将[CompilationUnit]作为结构的根。编译单元表示单个文件中的所有Dart代码
4,如上所述,语法中的每个生成都有"AstNode"的子类。子类在"package:analyszer/dart/ast/ast.dart"中定义。
sdk/pkg/kernel/binary.md结构说明
5,每个节点类都通过getter提供对其父节点和子节点的访问。例如,类"BinaryExpression"定义了getter"parent"、"leftOperand"和"rightOperand"。它还为作为构造一部分(但不是子构造的一部分)的token提供getter。例如,在二进制表达式中,有一个getter来访问"operator"。
6,每个节点类和每个token都携带位置信息。您可以要求从包含文件的开头开始输入entity开头的字符"offset",以及字符"length"。对于AST节点,偏移量是结构中第一个token的偏移量,长度包括结构中最后一个token的结尾。第一个标记之前或最后一个标记之后的任何空格都被视为父节点的一部分。
三,语义分析:
1,语义是程序的实际意义,例如:一个变量的类型,一个函数的返回值,一个表达式的结果等。语法树只记录了程序的语法结构,而不包含语义信息。因此,我们需要借助语义分析(Semantic Analysis)来获取语义信息。语义分析通过对语法树上的节点进行扫描和检查,来生成语义信息,并且给语法树上的节点附加语义信息
2,定义元素模型。元素模型描述了 Dart 代码的语义(而不是句法)结构。元素模型包含两种密切相关的对象:元素([Element] 的子类的实例)和类型。此库定义元素,类型在 [type.dart](../dart_element_type/dart_element_type-library.html)中定义,一般来说,一个元素代表代码中声明的东西,例如类,方法或变量。元素以树形结构组织,父元素的子元素是逻辑上(通常也是语法上)属于父元素声明的元素。例如,表示类中的方法和字段的元素是表示类的元素的子元素,每个完整的元素结构都由 [LibraryElement] 类的实例根据。库元素表示一个 Dart 库。每个库都由一个或多个编译单元(库及其所有部分)定义。编译单元由 [CompilationUnitElement] 类表示,并且是它们定义的库的子元素。每个编译单元可以包含零个或多个顶级声明,例如类,函数和变量。这些每个都表示为是编译单元的子元素的元素
3,打印输出:
void printMembers(CompilationUnitElement unitElement) {
for (ClassElement classElement in unitElement.classes) {
print(classElement.name);
for (ConstructorElement constructorElement in classElement.constructors) {
if (!constructorElement.isSynthetic) {
print(" ${constructorElement.displayName}");
}
}
for (FieldElement fieldElement in classElement.fields) {
if (!fieldElement.isSynthetic) {
print(" ${fieldElement.name}");
}
}
for (PropertyAccessorElement accessorElement in classElement.accessors) {
if (!accessorElement.isSynthetic) {
print(" ${accessorElement.name}");
}
}
for (MethodElement methodElement in classElement.methods) {
if (!methodElement.isSynthetic) {
print(" ${methodElement.name}");
}
}
}
}
四,源码分析:
1,主要文件包括:
parser.dart(语法)
parser_impl.dart(语法)
token.dart
listener.dart
ast_builder.dart
xxx_builder.dart,xxx_listen.dart
ast.dart(包含类AstNode,CompilationUnit,AnnotatedNode等定义,也包括如何打印输出ast结构视图)
type.dart(语义)
pkg/analyzer/lib/src/dart/element/element.dart(语义)
ast_to_binary.dart
ast_from_binary.dart
2,工具:
通过gen_kernel,dump_kernel工具查看源码转ast的字节码为:
网上有通过修改代码,输出清晰易懂的格式
3,一些主要的类定义
上面已介绍token的类定义,这里介绍astnode,编译单元等定义
abstract class AstNode implements SyntacticEntity {
Token get beginToken;
Iterable get childEntities;//返回一个迭代器,该迭代器可用于遍历构成该节点内 容的所有实体(AST节点或token),包括文档注释,但不包括其他注释
@override
int get end;//返回此节点源范围的最后一个字符之后的字符的偏移量。这相当于"node.getOffset()+node.getLength()"。对于编译单元,这将等于 该单元源代码的长度。对于合成节点,这将等同于节点的偏移量(因为长度定义为零(0))
Token get endToken;
@deprecated
E getAncestor(Predicate predicate);//返回此节点的最直接祖先,对于该节点,[predice]返回"true",如果没有这样的祖先,则返回"null"。请注意,永远不会返回此节点
void visitChildren(AstVisitor visitor);//用给定的[visitor]访问此节点的所有子节点。孩子们将按词汇顺序进行访问
等等等…
}
gen_kernel.dart流程图如下:
备注:一些基本概念
编译器 IR (Intermediate Representation) 中间代码是编译器在编译过程中生成的代码,它在源代码与目标代码之间。
IR 中间代码的目的是将源代码转换为编译器可以理解的形式,以便在编译过程中进行优化和生成目标代码。
IR 中间代码的格式通常是一种类似汇编代码的形式,它是一种低级的,机器无关的代码,不依赖特定的处理器架构。
IR 中间代码的主要优点是可移植性和可重用性,因为它不依赖特定的处理器架构,可以在多种平台上使用。它还具有更好的可读性和可调试性,因为它比目标代码更加接近高级语言。
编译器生成 IR 中间代码后,还可以进行优化,例如常量传播、简化表达式等。这些优化有助于生成更高效的目标代码。最后,编译器将 IR 中间代码转换为目标代码,以便在特定的处理器上执行。
IL 是编译器将 IR 转换为的目标代码的中间表示
AST 是从源代码中构建的语法树,它将程序语法结构转化为树形结构,并在语义分析阶段中对程序进行验证。
而 IR 是从 AST 中生成的,专为机器码生成优化而设计的,它比 AST 更接近机器语言,通常包含更多的低层次信息,如寄存器分配,代码优化等。 IR 的目的是为后续的机器码生成阶段提供便利。
AST(Abstract Syntax Tree)是源代码编译过程中生成的一种抽象语法树结构,表示源代码的语法结构.
CFG(Control Flow Graph)是控制流图,它表示程序代码中语句的执行顺序. CFG是一种图结构,节点表示程序语句,边表示语句间的执行顺序.
SSA(单一赋值程序)是一种编译原理中的算法,用于分析和优化程序的运行时行为。它的基本思想是:对于每个变量,确保其在程序的任何时刻只有一个有效的值。为了实现这个目标,SSA算法会在代码中建立一个新的临时变量,来表示每个变量的新值,从而保证每个变量只有一个值。在Dart SDK中,SSA算法通常用于分析程序的静态数据流和生成高效的代码。例如,SSA可以分析变量的生存期并自动优化变量的分配,以减少寄存器和内存使用。SSA还可以检测程序中的内存错误,例如数组越界或使用未初始化的变量,并生成警告或错误信息。
总之,SSA是Dart SDK中一种重要的编译优化算法,它可以提高程序的性能和正确性,同时也帮助开发人员更好地理解程序的行为。
后续分析:
Ast到函数控制流(CFG),因为ast只是语法结构,需要通过CFG控制流程
灭绝的生物能复活吗?恐龙猛犸象这些庞然大物能够复活吗?很多人都想过这个问题,但答案是,可能性很低。因为这些动物已经灭绝很久了,特别是恐龙,几乎已经找不到完整的恐龙基因了,也就没法复活。丹麦生物学家汤姆
尾巴超长,云南发现2。44亿年前长尾红河龙化石新华社北京5月11日电(记者金地张泉)全长47厘米形似水生蜥蜴,超长的尾巴尤其特别在云南省红河州,科学家发现了一种约2。44亿年前的海生爬行动物,命名为长尾红河龙。这是目前我国发现
走路时膝盖突然发软,这就是缺钙吗?医生这4个原因或许很常见爱乐养生不知道大家在日常生活当中,有没有遇到过这样的问题,那就是在正常走路或者是上下楼梯的时候,突然感觉到膝关节吃不住劲,突然晃一下的感觉,还会出现疼痛感,腿随即就会出现发软的情况
为什么茶会越喝越干呢?喝茶,自然是喝茶水。原本来说,喝茶之后,本应该是解渴。而有些茶友却反馈,喝茶之后越喝越渴,嗓子还越来越干拔干还是涩感喝完茶后,口腔里有时会出现干燥的感觉,像是吃了没熟透的香蕉,舌头
理想汽车被曝毁约校招生,官方回应因业务调整,提供调岗或赔偿Tech星球5月11日消息,近日有网友在社交平台曝光,称理想汽车毁约校招生,据网友曝光的邮件内容透露,解约原因是由于理想汽车近期对业务架构进行调整,目前公司没有相关匹配的岗位。对此
你的凉鞋该换了,今夏跪求你穿这双鞋接近夏天,我们工作室的姑娘鞋子也买了不少,不过几乎没怎么见她们穿出来过。问了才知道,80的人都是买完不会搭,只能压箱底。确实,别看鞋子占比小,但这就是决定穿搭成败的关键之举。穿对了
丝柔五月MANITO母爱赞礼母亲节来临,用MANITO向最爱的她,表达最柔美的祝福浅艾绿色真丝流光交汇金色蕾丝重现1920年代复古之梦被她的魅力所包围华丽与繁复的极致之美为初夏添一抹浪漫色彩MANITOAll
几款军事风夹克推荐说到军事风夹克,很多男性朋友应该知道m51,m65,og107热带丛林夹克等等,因为在电影你经常看到。m51夹克设计总体来说看起来比较能够日常化地穿着,特别是翻领,里面比较好搭配衬
夏日里的幸运红,这些搭配示范,告别土气,拉满时尚感你好呀,我是Annie在曾经的人生低谷时期,大师指出红色是博主的幸运红,红色代表着吉祥喜庆热烈幸福勇气,会带来好运因此,博主近年来入了不少红色的单品,如果你也感兴趣的话,我们就一起
脚底几个反射区很重要通过脚底按摩的方法能够刺激脚上相关反射区促进患者自身胰脏的活力,进而促进胰岛素的分泌,防治糖尿病。以下就跟大家介绍一下脚底按摩疗法的具体步骤先用双手从足趾到小腿做一般按摩,使足及小
香山岩记忆香山岩在翔安区东南部,是一处宗教圣地。据说香山是朱熹在同安任主簿时命名的。久闻香山岩大名,知道那边香火很旺,一直很想去那边看看。终于,有一次做小城镇的课题,那天在马巷调研,我们利用