深入V8引擎-AST(1)
阿新 • • 發佈:2019-07-01
沒辦法了,開坑吧,接下來的幾篇會講述JavaScript字串原始碼在v8中轉換成AST(抽象語法樹)的過程。
JS程式碼在V8的解析只有簡單的幾步,其中第一步就是將源字串轉換為抽象語法樹,非常類似於vue中將html轉換為VNODE的過程。該過程涉及的類並不多,均位於/src/parsing資料夾中,包括parsing、parser、scanner、token等等,先簡單介紹一下各類的作用。
- Parser => 核心類,掌管了整個轉換過程,內部包含scanner、parse-info等私有屬性,產出抽象語法樹
- Parsing => 這只是名稱空間,全稱為v8::interval::parsing,提供ParseProgram、ParseFunction、ParseAny作為parse的入口方法
- ParseInfo => 編譯資訊的描述類,包含大量配置引數與編譯後的容器
- Scanner => 負責逐字解析解析源字串的類,提供比較上層的API,初始化的Initialize方法會初始化所有變數並步進一格
- Scanner-character-streams => 所有源字串都需要根據型別轉換為該類,這個類提供解析的實際操作,諸如Peek(返回當前解析位置項)、Advance(解析過程前進一步)等等
- Token => 包含所有抽象語法樹型別的列舉,例如關鍵詞(const、if)、符號((、{)等等
這些所有的類通過互相合作,最後產出一個型別為FunctionLiteral的結果,將其傳入asm模組,生成底層程式碼。
型別的繼承關係樹如下。
其實發現這個過程還是挺痛苦的,因為從Compile一路看下來,發現直接就進了asm變成了組合語言,可以說一切來的那麼突然,我根本找不到突破點。當然,如果去掉一些無關的配置和CHECK,可以找到編譯核心屬性,比如說最後的AsmJs部分是這樣呼叫的。
MaybeHandle<SharedFunctionInfo> GenerateUnoptimizedCodeForToplevel( Isolate* isolate, ParseInfo* parse_info, AccountingAllocator* allocator, IsCompiledScope* is_compiled_scope) { // ... std::vector<FunctionLiteral*> functions_to_compile; functions_to_compile.push_back(parse_info->literal()); while (!functions_to_compile.empty()) { FunctionLiteral* literal = functions_to_compile.back(); functions_to_compile.pop_back(); Handle<SharedFunctionInfo> shared_info = Compiler::GetSharedFunctionInfo(literal, script, isolate); if (shared_info->is_compiled()) continue; if (UseAsmWasm(literal, parse_info->is_asm_wasm_broken())) { std::unique_ptr<UnoptimizedCompilationJob> asm_job( AsmJs::NewCompilationJob(parse_info, literal, allocator)); if (asm_job->ExecuteJob() == CompilationJob::SUCCEEDED && FinalizeUnoptimizedCompilationJob(asm_job.get(), shared_info, isolate) == CompilationJob::SUCCEEDED) { continue; } } // ... } // ... return top_level; }
鬼一樣的程式碼,只看最後返回的話,可以看出所有的呼叫都涉及那個literal。
而這個literal是parse_info的一個屬性,初始化時是NULL,在compile的某一步一定進行處理了,於是回頭去翻了一遍整個編譯過程。
最後終於在CompileTopLevel找到了關鍵的一行程式碼。
if (parse_info->literal() == nullptr && !parsing::ParseProgram(parse_info, isolate)) { return MaybeHandle<SharedFunctionInfo>(); }
而這裡,就是解析原始碼成抽象語法樹的地方,後面會從這裡入手,邊看邊寫