文档路径pkg/analyzer/doc/md
一,AST的结构
1,AST由排列成树的节点(“AstNode”的实例)组成。也就是说,每个节点可以有零个或多个子节点,每个子节点也是一个节点,而根节点以外的每个节点都只有一个父节点。
2,树的根通常是“CompilationUnit”。编译单元具有表示语言构造(如导入指令和类声明)的子级。类声明包含表示类的每个成员的子级。节点的结构与Dart语言语法相似,但不完全相同
3,如上所述,语法中的每个生成都有“AstNode”的子类。子类在“package:analyszer/dart/ast/ast.dart”中定义。
4,每个节点类都通过getter提供对其父节点和子节点的访问。例如,类“BinaryExpression”定义了getter“parent”、“leftOperand”和“rightOperand”。它还为作为构造一部分(但不是子构造的一部分)的token提供getter。例如,在二进制表达式中,有一个getter来访问“operator”。
5,每个节点类和每个token都携带位置信息。您可以要求从包含文件的开头开始输入entity开头的字符“offset”,以及字符“length”。对于AST节点,偏移量是结构中第一个token的偏移量,长度包括结构中最后一个token的结尾。第一个标记之前或最后一个标记之后的任何空格都被视为父节点的一部分。
6,类定义:
AstNode类,继承SyntacticEntity(语法实体),一个AST结构代表一个a Dart program
路径:pkg/analyzer/lib/dart/ast/ast.dart
备注: 客户端不能在此类中扩展、实现或混合
abstract class AstNode implements SyntacticEntity {
/// Return the first token included in this node's source range.
Token get beginToken;
返回一个迭代器,该迭代器可用于遍历构成该节点内容的所有实体(AST节点或token),包括文档注释,但不包括其他注释
Iterable<SyntacticEntity> get childEntities;
返回此节点源范围的最后一个字符之后的字符的偏移量。这相当于“node.getOffset()+node.getLength()”。对于编译单元,这将等于 该单元源代码的长度。对于合成节点,这将等同于节点的偏移量(因为长度定义为零(0))
@override
int get end;
/// Return the last token included in this node's source range.
Token get endToken;
如果此节点是合成节点,则返回“true”。合成节点是解析器为了从代码中的错误中恢复而引入的节点。合成节点的长度始终为零(“0”)
bool get isSynthetic;
@override
int get length;
@override
int get offset;
…
返回此节点的最直接祖先,对于该节点,[predice]返回“true”,如果没有这样的祖先,则返回“null”。请注意,永远不会返回此节点
@deprecated
E getAncestor<E extends AstNode>(Predicate<AstNode> predicate);
…
以近似有效源的形式返回此节点的文本描述。返回的字符串将不是有效的源,主要是在节点本身格式不正确的情况下
String toSource();
用给定的[visitor]访问此节点的所有子节点。孩子们将按词汇顺序进行访问
void visitChildren(AstVisitor visitor);
}
7,结构图:
二,类型
1,类型模型表示语言规范定义的类型信息
2,类型的种类(有四个类型类,它们都是抽象类“DartType”的子类型)
### Interface Types
### Function Types
### The Void Type
### The Type Dynamic
## Accessing Types
3,有两种方法可以获取“DartType”的实例:从[AST][AST]和来自[element][element]模型
在已解析的AST中,每个表达式都有一个非“null”“staticType”。
三,element
1,element模型:
元素模型与[type][type]模型一起描述了Dart代码的语义(而不是句法)结构。代码的语法结构由[AST][AST]建模。
一般来说,元素表示代码中声明的东西,例如类、方法或变量。元素可以显式声明,例如由类声明定义的类,也可以隐式声明,比如为没有任何显式构造函数声明的类定义的默认构造函数。隐式声明的元素称为_synthetic_元素。
有几个元素表示未声明的实体。例如,有一个元素表示编译单元(`.dart`文件)和另一个表示库。
2,element模型结构
元素以树结构组织,其中元素的子元素是父元素声明的逻辑(通常是语法)部分。
例如,表示类中方法和字段的元素是表示类的元素的子元素。
每个完整的元素结构都是由类“LibraryElement”的一个实例创建的。
库元素表示单个Dart库。每个库由一个或多个编译单元(库及其所有部分)定义。
编译单元由类“CompilationUnitElement”表示,并且是由它们定义的库的子级。每个编译单元可以包含零个或多个顶级声明,例如类、函数和变量。这些元素中的每一个依次表示为编译单元的子元素。类包含方法和字段,方法可以包含局部变量等。
元素模型不包含代码中的所有内容,只有代码声明的那些东西。例如,它不包括方法体中语句的任何表示,但如果其中一个语句声明了局部变量,则该局部变量将由元素表示。
四,token 流
token是一个双链接列表,在开头和结尾都有特殊的“EOF”token。
结束处的EOFtoken指向自身作为下一个token,而开始处的EOF token指向自己作为上一个token。因此,对于一个格式正确的token流(我们通常假设总是这样),上一个和下一个都不能返回null。我们无法在类型系统中表达这一点,因为在构建列表时,这些字段将暂时为空。因此,整个代码库的惯例是在调用这两个getter的任何地方都用bang(!)作为后缀。token通常通过AST访问,参考源码文件:pkg/front_end/lib/src/scanner/token.dart
1,表示在源文件中具有位置和range的语法实体(token或AST节点)的接口。
abstract class SyntacticEntity {
/**
* Return the offset from the beginning of the file to the character after the
* last character of the syntactic entity.
*/
int get end;
/**
* Return the number of characters in the syntactic entity's source range.
*/
int get length;
/**
* Return the offset from the beginning of the file to the first character in
* the syntactic entity.
*/
int get offset;
}
2,从输入扫描的Token。每个Token都知道哪些Token在它之前和之后,充当双链接Token列表中的链接。
客户端不能在此类中扩展、实现或混合
abstract class Token implements SyntacticEntity {
/**
初始化新创建的Token,使其具有给定的[类型]和[偏移量]
*/
factory Token(TokenType type, int offset, [CommentToken preceedingComment]) =
SimpleToken;
/**
* The number of characters parsed by this token.
*/
int get charCount;
/**
* The character offset of the start of this token within the source text.
*/
int get charOffset;
/**
* The character offset of the end of this token within the source text.
*/
int get charEnd;
/**
* The token before this synthetic token,
* or `null` if this is not a synthetic `)`, `]`, `}`, or `>` token.
*/
Token get beforeSynthetic;
/**
* Set token before this synthetic `)`, `]`, `}`, or `>` token,
* and ignored otherwise.
*/
set beforeSynthetic(Token previous);
@override
int get end;
/**
* The token that corresponds to this token, or `null` if this token is not
* the first of a pair of matching tokens (such as parentheses).
*/
Token get endGroup => null;
/**
* Return `true` if this token represents an end of file.
*/
bool get isEof;
/**
* True if this token is an identifier. Some keywords allowed as identifiers,
* see implementation in [KeywordToken].
*/
bool get isIdentifier;
/**
* True if this token is a keyword. Some keywords allowed as identifiers,
* see implementation in [KeywordToken].
*/
bool get isKeyword;
/**
* True if this token is a keyword or an identifier.
*/
bool get isKeywordOrIdentifier;
/**
* Return `true` if this token is a modifier such as `abstract` or `const`.
*/
bool get isModifier;
/**
* Return `true` if this token represents an operator.
*/
bool get isOperator;
/**
* Return `true` if this token is a synthetic token. A synthetic token is a
* token that was introduced by the parser in order to recover from an error
* in the code.
*/
bool get isSynthetic;
/**
* Return `true` if this token is a keyword starting a top level declaration
* such as `class`, `enum`, `import`, etc.
*/
bool get isTopLevelKeyword;
/**
* Return `true` if this token represents an operator that can be defined by
* users.
*/
bool get isUserDefinableOperator;
/**
* Return the keyword, if a keyword token, or `null` otherwise.
*/
Keyword get keyword;
/**
* The kind enum of this token as determined by its [type].
*/
int get kind;
@override
int get length;
/**
* Return the lexeme that represents this token.
*
* For [StringToken]s the [lexeme] includes the quotes, explicit escapes, etc.
*/
String get lexeme;
/**
* Return the next token in the token stream.
*/
Token get next;
/**
* Return the next token in the token stream.
*/
void set next(Token next);
@override
int get offset;
/**
* Set the offset from the beginning of the file to the first character in
* the token to the given [offset].
*/
void set offset(int offset);
/**
返回此标记之前的注释列表中的第一个注释,如果此标记之前没有注释,则返回“null”。通过使用[next]跟踪token流,直到返回“null”,可以获得其他注释。例如,如果原始内容是“/*one*//*two*/id”,那么前面的第一个注释标记将具有“/*one*/”的词位,而下一个注释标记的词位将为“/*two*/”
*/
Token get precedingComments;
/**
对于符号和关键字标记,返回此标记表示的字符串值。对于[StringToken],此方法返回[:null:]。对于[SymbolToken]s和[KeywordToken]s,字符串值是源自[TokenType]或[Keyword]实例的编译时间常数。这允许使用[:idential:]测试关键字和符号,例如[:idential('class',token.value):]。请注意,为字符串标记返回[:null:]对于识别符号和关键字很重要,我们不能使用[lexeme]。字符串文字“$a($b)”产生…,SymbolToken($),StringToken(a),StringToken(()。。。
解析标识符“a”后,解析器使用[:idential(next.stringValue,'('):]测试函数声明,该声明(正确地)返回false,因为stringValue返回[:null:]。
*/
String get stringValue;
/**
* Return the type of the token.
*/
TokenType get type;
}
newLocation_fromUnit
ParseStringResultImpl
StackListener
beginCompilationUnit
ast_to_binary,ast_from_binary
ast_binary_writer.dart
六,从源码到ast流程
1,源码->token(scanner)->ast(parse)
2,几个重要的类定义
CompilationUnit类(编译单元)。
虽然语法限制编译单元中指令和声明的顺序,但此类不强制执行这些限制。特别是,即使词汇顺序不符合语法的限制,也将按词汇顺序访问编译单元的子级.
/*
compilationUnit ::= directives declarations
directives ::= [ScriptTag]? [LibraryDirective]? namespaceDirective* [PartDirective]* | [PartOfDirective]
namespaceDirective ::= [ImportDirective] | [ExportDirective]
declarations ::= [CompilationUnitMember]
*/
路径pkg/analyzer/lib/dart/ast/ast.dart
abstract class CompilationUnit implements AstNode {
/// Set the first token included in this node's source range to the given
/// [token].
void set beginToken(Token token);
/// Return the declarations contained in this compilation unit.
NodeList<CompilationUnitMember> get declarations;
/// Return the element associated with this compilation unit, or `null` if the
/// AST structure has not been resolved.
CompilationUnitElement get declaredElement;
/// Return the directives contained in this compilation unit.
NodeList<Directive> get directives;
/// Return the element associated with this compilation unit, or `null` if the
/// AST structure has not been resolved.
@deprecated
CompilationUnitElement get element;
/// Set the element associated with this compilation unit to the given
/// [element].
void set element(CompilationUnitElement element);
/// Set the last token included in this node's source range to the given
/// [token].
void set endToken(Token token);
此编译单元可用的一组功能,如果未知,则为“null”,由.packages文件、封装包的SDK版本约束和/或文件顶部注释中存在“@dart”指令的某种组合决定。例如,这个’null’就是根据[CompilationUnit]重新合成的
FeatureSet get featureSet;
/// Return the line information for this compilation unit.
LineInfo get lineInfo;
/// Set the line information for this compilation unit to the given [info].
void set lineInfo(LineInfo info);
/// Return the script tag at the beginning of the compilation unit, or `null`
/// if there is no script tag in this compilation unit.
ScriptTag get scriptTag;
/// Set the script tag at the beginning of the compilation unit to the given
/// [scriptTag].
void set scriptTag(ScriptTag scriptTag);
/// Return a list containing all of the directives and declarations in this
/// compilation unit, sorted in lexical order.
List<AstNode> get sortedDirectivesAndDeclarations;
}
ParserAstNode类
abstract class ParserAstNode {
final String what;
final ParserAstType type;
Map<String, Object?> get deprecatedArguments;
List<ParserAstNode>? children;
ParserAstNode? parent;
ParserAstNode(this.what, this.type);
// TODO(jensj): Compare two ASTs.
}
CompilationUnitEnd类
路径:front_end/lib/src/fasta/util/parser_ast_helper.dart
class CompilationUnitEnd extends ParserAstNode {
final int count;
final Token token;
CompilationUnitEnd(ParserAstType type,
{required this.count, required this.token})
: super("CompilationUnit", type);
@override
Map<String, Object?> get deprecatedArguments => {
"count": count,
"token": token,
};
}
源码到AST流程图:
八,从ast到CFG(控制流图(Control Flow Graph))
正统做法推荐的是在做数据流分析之前,先把AST转换为一种更细粒度的、把控制流显式暴露出来的中间表示(IR)
九:补充dill文件
源码:
void main() {
ConsolePrinter cp= new ConsolePrinter();
cp.print_data();
}
printMsg()=>print("hello");
int test()=>123;
class Printer {
void print_data() {
print(test());
}
}
class ConsolePrinter implements Printer {
void print_data() {
printMsg();
}
}
dill文件:
dart支持增量编译(incremental compilation),修改部分代码,不需要全部重新编译,只编译相关部分.支持热重载(hotReload),可参考博客:https://cloud.tencent.com/developer/article/1964443?from=article.detail.1405079&areaSource=106000.1&traceId=NJ7ptKaT4spfIAxVSAWsa,本人未实践