花了5篇才把一个字符串词法给解析完,不知道要多久才能刷完整个流程,GC、复杂数据类型的V8实现那些估计又是几十篇,天呐,真是给本身挖了个大坑。segmentfault
前面几篇实际上只是执行了scanner.Initialize方法,并未开始全面解析,继续跑流程。app
FunctionLiteral* Parser::ParseProgram(Isolate* isolate, ParseInfo* info) { // ... /** * 前面完成了这一步 */ scanner_.Initialize(); if (FLAG_harmony_hashbang) { scanner_.SkipHashBang(); } // 接下来进这个方法 FunctionLiteral* result = DoParseProgram(isolate, info); // ... return result; }
后面的方法域都在Parser类下,毕竟这是整个AST的执行类,parseInfo只是一个编译信息的存储类,直接进DoParseProgram方法。函数
FunctionLiteral* Parser::DoParseProgram(Isolate* isolate, ParseInfo* info) { // ... FunctionLiteral* result = nullptr; { // ... ScopedPtrList<Statement> body(pointer_buffer()); int beg_pos = scanner()->location().beg_pos; if (parsing_module_) { // ... } else if (info->is_wrapped_as_function()) { ParseWrapped(isolate, info, &body, scope, zone()); } else { // 进入这里 this->scope()->SetLanguageMode(info->language_mode()); ParseStatementList(&body, Token::EOS); } // ... } // ... return result; }
这里对编译描述类进行查询,判断待编译字符串是不是模块、包装函数,会进入不一样的分支,因为测试代码只是两个字符串相加,因此进入最后的分支。测试
void ParserBase<Impl>::ParseStatementList(StatementListT* body, Token::Value end_token) { // ... /** * 当以前初始化的第一个Token是字符串时 * 判断多是设置模式 */ while (peek() == Token::STRING) { bool use_strict = false; bool use_asm = false; Scanner::Location token_loc = scanner()->peek_location(); /** * 作字符串严格断定 * 声明相似于"use strict"、"use \nstrict"、"use \x73trict"都是不合法的 */ if (scanner()->NextLiteralExactlyEquals("use strict")) { use_strict = true; } else if (scanner()->NextLiteralExactlyEquals("use asm")) { use_asm = true; } // 这里提早进行解析 StatementT stat = ParseStatementListItem(); // ... if (use_strict) { // 进入严格模式 } else if (use_asm) { // 进入asm模式 } else { // 非配置字符串 进入普通模式 RaiseLanguageMode(LanguageMode::kSloppy); } } TargetScopeT target_scope(this); /** * 其余状况全面解析AST */ while (peek() != end_token) { StatementT stat = ParseStatementListItem(); if (impl()->IsNull(stat)) return; if (stat->IsEmptyStatement()) continue; body->Add(stat); } }
这一步v8对以前解析的第一个词法进行了判断,来选择是否开启严格模式或者asm模式,能够看一眼严格模式定义。ui
To invoke strict mode for an entire script, put the exact statement
"use strict";
(or'use strict';
) before any other statements.this
因为这个语句必须出如今最前面,因此会在进行全面转换前判断待编译字符串的第一个Token是否是严格等于这个字符串,来设置编译模式。spa
这里还有另一个新鲜的配置,即"use asm",大部分人不会接触到这个东西,asm是JavaScript的一个子集,虽然语法总的来讲也是JS的,可是很是难阅读,对于机器来讲更加友好。若是用了这个配置,v8会跳过一些阶段,直接将代码编译成机器指令(贴一个介绍连接)。指针
若是不配置模式,v8会以普通模式来编译代码,随后底部的while循环指向了全面的AST转换。code
先简单介绍一下AST的容器body,初始化代码以下。blog
/** * 构造函数参数是一个指针 指向一个内容为任意指针的vector * std::vector<void*>* pointer_buffer() { return &pointer_buffer_; } * std::vector<void*> pointer_buffer_; * ScopedPtrList<Statement>的简写类型为StatementListT 定义以下 * using StatementListT = typename Types::StatementList; * using Types = ParserTypes<Impl>; * struct ParserTypes<Parser> { using StatementList = ScopedPtrList<v8::internal::Statement>; } */ ScopedPtrList<Statement> body(pointer_buffer());
这里的类型包含两部分,一个是容器类ScopePtrList,一个是内容Statement,先看容器。
template <typename T> class ScopedPtrList final { public: void Add(T* value) { buffer_.push_back(value); ++end_; } private: std::vector<void*>& buffer_; size_t start_; size_t end_; }
象征性的给一些属性和方法,传统的vector一把梭,知道一个Add方法就差很少了,反正都那样。
至于Statement类则是标准的抽象语法树节点类,简单看一下定义就好了,其自己没有什么东西,大量的枚举类型都定义在父类上,暂时不展开。
class Statement : public AstNode { protected: Statement(int position, NodeType type) : AstNode(position, type) {} static const uint8_t kNextBitFieldIndex = AstNode::kNextBitFieldIndex; };
转换的AST会以Statement类存储,并经过Add方法加到容器中。
这一篇感受啥都没写,先这样吧,下一篇开始正式全面解析AST。