花了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。