原本打算是把node源碼看得差不多了再去深入V8的,但是這兩者基本上沒辦法分開講。
與express是基於node的封裝不同,node是基於V8的一個應用,源碼內容已經滲透到V8層面,因此這章簡述一下我目前理解的V8引擎吧。
首先需要理解的是V8是一個JS代碼運行平台,可以將JS代碼編譯執行。
本節就非常淺顯的講一下V8內部一些常見類,以及一個運行JS代碼的簡單demo。
(由於研究V8引擎原理的人非常多,本人學識淺薄,可以去參考別人的博客)
參考資料:
1、很多大佬的博客
2、V8引擎API文檔:https://v8docs.nodesource.com/
3、github:https://github.com/v8/v8
本節先列舉一些核心類,示例代碼大部分來源於node中的源碼。
Isolate
該類代表一個V8引擎實例,有自己獨立的狀態,用法如下。
1、不能使用new關鍵字來生成一個實例,只能通過類方法Isolate::New(params)來創建。
Isolate* const isolate = Isolate::New(params);
2、該類的方法都是設置V8引擎的一些處理細節。
// 添加error的信息監聽器 isolate->AddMessageListener(OnMessage); // 從名字能看出來 設置未捕捉中斷異常的回調函數 isolate->SetAbortOnUncaughtExceptionCallback(ShouldAbortOnUncaughtException); // 設置Microtask的執行方式(有三種) isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit); // 設置致命錯誤的回調函數 isolate->SetFatalErrorHandler(OnFatalError); // WebAssembly代碼生成回調函數 isolate->SetAllowWasmCodeGenerationCallback(AllowWasmCodeGenerationCallback);
3、作為一個參數傳入其余的V8工具類中。
// 單線程運行V8的Isolate Locker locker(isolate); Isolate::Scope isolate_scope(isolate);
Local/Persistent - Handle
這個地方我之前一直比較混亂,因為有文章指出:Handle類定義在v8.h中,它是一個模板類,而且有兩個派生類Local和Persistent。
出處:https://blog.csdn.net/sunbxonline/article/details/20310897
但是從源碼來看,無論是Local<T>還是MaybeLocal<T>,均不繼承於任何類(在V8中確實存在一個Handle的類,但是跟這兩個沒有繼承關系)。
這是因為V8版本不一致,所以我這里只講當前版本的情況,源碼注釋如下:
#if !defined(V8_IMMINENT_DEPRECATION_WARNINGS) // Handle is an alias for Local for historical reasons. template <class T> using Handle = Local<T>; #endif
這兩個類從作用上講都是handle,但實際上並不繼承於同一個父類。
1、Local/Persistent是V8的兩個類,指向底層的原始數據。
2、所有對象的引用都需要被V8的垃圾回收管理,在管理中可能出現移動對象的情況(參考網上大量關於V8垃圾回收的博客),這會導致對象指針產生錯誤,所以不能直接使用原始的數據類型,諸如String,而需要使用Local<String>,Local被V8引擎管理,會在對象移動時更新指針指向,並在合適的時候進行回收。
3、Persistent屬於全局對象(可參考Global),獨立於HanldeScope,可使用Reset方法清空。
Value
所以JS數據類型映射到C++的根類,繼承關系如下:
具體的內部實現后面做分析。
HandleScope
一個管理handle的容器,在當前作用域開頭聲明一個HanldeScope,在域結束時會自動清理所有的handle。
HandleScope handle_scope(isolate);
嵌套使用時,作用域會自動進行切換。
Context
執行上下文,有自己獨立的函數與對象。與Isolate相似,通過類方法New來生成。
auto context = Context::New(isolate, nullptr, object_template);
可通過內部Scope類來進行上下文的切換。
Context::Scope context_scope(context);
Script
該類主要負責對JS代碼字符串進行編譯和執行,核心方法為Compile、Run。
Script::Compile可以編譯JS代碼字符串,返回一個Local<Script>對象
Script::Run可以執行編譯后的JS代碼,返回一個Handle<Value>對象
另外,還有FunctionTemplate/ObjectTemplate可以封裝C++的對象、函數提供給JS代碼調用,示例代碼如下:
// 將C++的GetBinding函數包裝提供給JS代碼調用 // NewFunctionTemplate是v8::FunctionTemplate::New()方法的包裝 v8::Local<v8::Function> get_binding_fn = env->NewFunctionTemplate(GetBinding)->GetFunction(env->context()) .ToLocalChecked();
基本上大部分用到的東西就是上面所列舉的,GC暫時不討論,以一個非常簡單的網上案例把上面的東西串起來:
// 創建一個Isolate實例 Isolate::CreateParams params; Isolate* const isolate = Isolate::New(params); // 創建一個HandleScope管理handle HandleScope handle_scope(isolate); // 創建一個上下文執行環境 Local<Context> context = Context::New(isolate); // 切換到當前上下文 Context::Scope context_scope(context); // 新建一個Local // 類型可以類比JS的源字符串 Local<String> source = v8::String::NewFromOneByte(isolate , "12345"); // 編譯該JS字符串 MaybeLocal<v8::Script> script = v8::Script::Compile(context , source); // 執行上面返回的編譯對象 Local<Value> result = script.ToLocalChecked()->Run(context).ToLocalChecked(); // 這個result就是對JS源字符串編譯執行后的C++代碼 Local<String> str = result->ToString(context ).ToLocalChecked();
這樣,對V8引擎就有了一個基本的認識,可以幫助我們更好的學習nodejs源碼。