深入出不來nodejs源碼-V8引擎初探


  原本打算是把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源碼。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM