V8 JavaScript引擎研究(四)在應用程序中使用V8

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。spa

能夠像這樣聲明持久對象:Persistent<Class>。指針

Contexts簡介

一個context表明一個執行環境上下文,容許單獨的、無關的JavaScript代碼運行在一個V8實例中。能夠指定JavaScript代碼運行在任何context中。code

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代碼中直接使用自定義的函數和對象。

相關文章
相關標籤/搜索