V8引擎在C++程序中使用簡介
V8引擎可以被嵌入在任何C++程序中使用。
V8的APIs提供了對JavaScript代碼的編譯和執行功能、與C++函數互掉、訪問數據結構、錯誤處理、及安全檢查等功能。在應用程序中可將V8當做一個C++庫來使用,訪問V8的APIs需要包含V8的頭文件v8.h。
使用V8非常簡單,首先看一個簡單示例程序:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "include/libplatform/libplatform.h" #include "include/v8.h" using namespace v8; class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { public: virtual void* Allocate(size_t length) { void* data = AllocateUninitialized(length); return data == NULL ? data : memset(data, 0, length); } virtual void* AllocateUninitialized(size_t length) { return malloc(length); } virtual void Free(void* data, size_t) { free(data); } }; int main(int argc, char* argv[]) { // 初始化V8 V8::InitializeICU(); V8::InitializeExternalStartupData(argv[0]); Platform* platform = platform::CreateDefaultPlatform(); V8::InitializePlatform(platform); V8::Initialize(); // 創建一個新的Isolate ArrayBufferAllocator allocator; Isolate::CreateParams create_params; create_params.array_buffer_allocator = &allocator; Isolate* isolate = Isolate::New(create_params); { Isolate::Scope isolate_scope(isolate); // 創建一個分配在棧上的handle scope. HandleScope handle_scope(isolate); // 創建一個context. Local<Context> context = Context::New(isolate); // 關聯context Context::Scope context_scope(context); // 創建一個包含JavaScript代碼的字符串 Local<String> source = String::NewFromUtf8(isolate, "'Hello' + ', World!'", NewStringType::kNormal).ToLocalChecked(); // 編譯JavaScript代碼 Local<Script> script = Script::Compile(context, source).ToLocalChecked(); // 運行代碼並產生結果 Local<Value> result = script->Run(context).ToLocalChecked(); String::Utf8Value utf8(result); printf("%s\n", *utf8); } // 釋放資源 isolate->Dispose(); V8::Dispose(); V8::ShutdownPlatform(); delete platform; return 0; }
- 一個isolate代表一個堆上的虛擬機實例。
- 一個handle是指向一個對象的指針,所有的V8對象都通過handle來訪問,這是為了便於垃圾回收器工作。
- 一個handle scope可以想象成是一系列handle的容器,當結束使用handle后,無需依次刪除每個handle,可直接簡單的刪除scope即可。
- 一個context代表一個執行環境上下文,允許單獨的、無關的JavaScript代碼運行在一個V8實例中。可以指定JavaScript代碼運行在任何context中。
Handles簡介
一個handle提供了一個對堆上對象地址的引用,V8垃圾回收器通過對釋放不再使用的對象來達到回收內存的作用。在垃圾回收周期期間,對象的位置可能發生移動,當一個對象發生移動后,所有指向它的handle都會被更新。
Handle的分類
分配在棧上的局部handle
生命周期由其所在的handle scope決定,當handle scope被刪除后,垃圾回收器將會回收其中的所有handle。這類handle典型使用在一個函數中。
可以像這樣聲明一個局部對象類:Local<Class>。
*注意:一個handle的棧不是C++調用棧的一部分,但handle scope是嵌入在C++調用棧中的。Handle scope只能通過棧內存來分配,不能使用new操作來分配。
分配在堆上的持久handle
當需要不僅是在局部保持對一個對象的引用時,使用堆handle。如在Chrome瀏覽器中,DOM節點都是使用堆handle。
可以像這樣聲明持久對象:Persistent<Class>。
Contexts簡介
一個context代表一個執行環境上下文,允許單獨的、無關的JavaScript代碼運行在一個V8實例中。可以指定JavaScript代碼運行在任何context中。
context機制的引入是非常必要的,JavaScript提供了非常多的內建函數和對象,由於這些函數和對象可以在JavaScript代碼執行時被改變,因此如果不提供一個單獨的執行環境context,將會產生許多問題。如兩個完全不相關的函數都修改了同一個內建的對象。
每次創建一個context都需要創建一系列的內建對象,這將產生很大的消耗。V8使用了緩存機制來解決這一問題,當創建完第一個context后,后續再需創建context時,代價非常小。V8同時使用了snapshot機制(可通過命令 snapshot=yes開啟,默認開啟)來高度優化第一次創建context所帶來的消耗。
當一個context被創建后,可以多次進出此context。例如,當在context A中時,進入了context B,此時當前的context更新為B,當退出B時,當前的context又被轉回A,見下圖所示:
在V8中使用context的絕佳實例是,每個瀏覽器中的每個iframe都使用一個單獨的context。
Templates簡介
Template被用來封裝C++函數和對象來給JavaScript對象使用。例如,Chrome封裝了C++中的DOM節點作為JavaScript對象。
Template分為函數template和對象template。
可以像下面這樣的方式來使用template:
Local<ObjectTemplate> global = ObjectTemplate::New(isolate); global->Set(String::NewFromUtf8(isolate, "log"), FunctionTemplate::New(isolate, LogCallback));
V8引擎的擴展和綁定
當JavaScript引擎所提供的能力不能滿足業務需求時,如引擎本身對HTML5的支持程度不夠,這時應用程序可以擴展V8引擎的能力。V8提供兩種方式來擴展引擎:動態的擴展機制以及靜態的綁定機制。
V8擴展
V8通過提供一個基類Extension和一個全局注冊函數,來擴展JavaScript借口。
示例代碼:
class MyExtension : public v8::Extension { public: virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(v8::Handle<v8::String> name) { // 可根據name來返回不同的函數 return v8::FunctionTemplate::New(MyExtension::MyFunc); } static v8::Handle<v8::Value> MyFunc(const v8::Arguments& args) { // 自定義處理 return v8::Undefined(); } }; MyExtension extension; RegisterExtension(&extension);
首先創建基於Extension基類的子類,然后實現GetNativeFunction。接着將子類的實例注冊到V8引擎中,這樣,當JavaScript代碼中調用MyFunc時,就能執行自定義的MyFunc函數。
整個過程非常簡單。
V8綁定
綁定的原理非常簡單,即使用IDL文件或接口文件來生成綁定文件,然后將這些文件與V8引擎一起編譯。
一個簡單的IDL文件如下:
module mymodule { interface [ InterfaceName=MyObject ] MyObj { readonly attribute long myAttr; DOMString myMethod(DOMString myArg); } }
完成好IDL文件后,可以使用WebKit的工具來通過將IDL文件轉換成V8的綁定文件。V8的綁定文件即是標准的C++文件,包含了在IDL文件中定義的函數和對象。當綁定文件連同V8一起編譯后,即可在JavaScript代碼中直接使用自定義的函數和對象。