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。

可以像这样声明持久对象: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代码中直接使用自定义的函数和对象。

posted @ 2016-09-29 15:41  WolfX21  阅读(5100)  评论(0编辑  收藏  举报