V8世界探险 (1) - v8 API概览

V8世界探险 (1) - v8 API概览

v8是Google开发的JavaScript引擎,自推出后就对js生态产生了巨大的影响。比如产生了运行在服务端的Node.js的巨大生态。

这么好玩的东西,我们当然要冲起去看看它的内部是如何实现的了。

Hello,v8 World!

首先好玩的一点是,v8可以作为一个库来嵌入到其它的应用中。

我们废话不多说,直接上代码.这是Google官方的调用在代码中嵌入一个v8引擎的例子,地址在:https://chromium.googlesource.com/v8/v8/+/branch-heads/4.8/samples/hello-world.cc

// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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[]) {
  // Initialize V8.
  V8::InitializeICU();
  V8::InitializeExternalStartupData(argv[0]);
  Platform* platform = platform::CreateDefaultPlatform();
  V8::InitializePlatform(platform);
  V8::Initialize();
  // Create a new Isolate and make it the current one.
  ArrayBufferAllocator allocator;
  Isolate::CreateParams create_params;
  create_params.array_buffer_allocator = &allocator;
  Isolate* isolate = Isolate::New(create_params);
  {
    Isolate::Scope isolate_scope(isolate);
    // Create a stack-allocated handle scope.
    HandleScope handle_scope(isolate);
    // Create a new context.
    Local<Context> context = Context::New(isolate);
    // Enter the context for compiling and running the hello world script.
    Context::Scope context_scope(context);
    // Create a string containing the JavaScript source code.
    Local<String> source =
        String::NewFromUtf8(isolate, "'Hello' + ', World!'",
                            NewStringType::kNormal).ToLocalChecked();
    // Compile the source code.
    Local<Script> script = Script::Compile(context, source).ToLocalChecked();
    // Run the script to get the result.
    Local<Value> result = script->Run(context).ToLocalChecked();
    // Convert the result to an UTF8 string and print it.
    String::Utf8Value utf8(result);
    printf("%s\n", *utf8);
  }
  // Dispose the isolate and tear down V8.
  isolate->Dispose();
  V8::Dispose();
  V8::ShutdownPlatform();
  delete platform;
  return 0;
}

我们从中可以看到几个重要的类:

  • V8:这个是个大杂烩的工具类,从初始化到关闭平台都离不开它。
  • ArrayBuffer是v8的数据类型,下面我们会来一张v8的数据结构图。
  • Isolate是v8中最重要的功能类,代表了一个v8引擎的实例。它不是线程安全的,如果在线程不安全情况下运行需要加锁。
  • Local是v8中存储对象的结构,代表了被GC管理的对象引用。与其对应的就是持久性对象,如Global和Persistent。
  • Context代表了在沙箱中运行的上下文。与Java一样,JavaScript也运行在一个沙箱安全环境中。
  • Script代表了运行在v8中的脚本。

上面使用的都是v8的公开API。这些API代表了v8的主要功能和主要数据结构。
我们先看一张v8的API的大图:

v8 api

对象与句柄

与C/C++与Rust等存在栈式管理的语言不同,JavaScript并没有栈上分配这样的概念。但是在v8的实现中,是有通过栈式管理作用域的数据结构,就是例程中我们所看到的HandleScope。
另外还有SealHandleScope类,将当前的HandleScope封装起来,强制需要创建新的HandleScope.

v8中的对象按生命周期可以分为两类,由GC管理的局部对象Local,还有持久性的对象。
持久性的对象分为:一次性的永续对象Eternal,和全局性的PersistentBase。
PersistentBase根据管理所有权方式的不同,采用赋值和复制方式的叫做Persistent,采用移动语义的叫做Global.
还有个UniquePersistent的模块类,其实是Global的别名。C++11中定义模板别名的方式,之前我们有文章讲过了:

template <class T> using UniquePersistent = Global<T>;

从例子中我们可以看到,通常我们使用的变量都是Local为主。不管是Context,String,Script还是Value都是通过Local模板来进行管理的:

    // Create a new context.
    Local<Context> context = Context::New(isolate);
    Local<String> source =
        String::NewFromUtf8(isolate, "'Hello' + ', World!'",
                            NewStringType::kNormal).ToLocalChecked();
    // Compile the source code.
    Local<Script> script = Script::Compile(context, source).ToLocalChecked();
    // Run the script to get the result.
    Local<Value> result = script->Run(context).ToLocalChecked();

现在并没有一个共同的Handle基类,Local,Eternal和PersistentBase之间没有共基类的关系。

由于返回值也大部分是Local,为了强制代码判空,v8经常使用MaybeLocal的模块,它是Local的一个封装。

我们再画一个图来回顾下它们之间的关系:

Handle & Scope

脚本相关的API

v8 script

脚本相关的API主要涉及到三个部分:源代码,编译后的脚本代码和编译器三部分。
源代码表示的类是ScriptOrigin和ScriptOriginOptions.
编译之后的脚本,按照是否跟上下文绑定,分为UnboundScript和Script两种。
编译器是ScriptCompiler.

另外,脚本的运行时信息也是很重要的结构,v8提供了包装错误信息的Message类,提供跟踪栈信息的StackTrace和StackFrame。

v8的数据类型

我们先看个数据类型的图,如果对于ES6不太熟悉的同学,可能对于某些ES6新增的类型会有些疑惑。不过不用担心,后面如果有会用到ES6的知识的话,我会尽可能做一些快餐式的简介。

v8数据类型

所有的v8数据类型都继承自Data类。
这个类非常简单:

/**
 * The superclass of values and API object templates.
 */
class V8_EXPORT Data {
  private:
  Data();
};

首先继承自Data的是一个跟js无关的NativeWeakMap:
同时让大家继续体验下几乎一切皆Local的感受,基本上每个类型都要加上Local<>模板管理一下:

/**
 * A map whose keys are referenced weakly. It is similar to JavaScript WeakMap
 * but can be created without entering a v8::Context and hence shouldn't
 * escape to JavaScript.
 */
class V8_EXPORT NativeWeakMap : public Data {
 public:
  static Local<NativeWeakMap> New(Isolate* isolate);
  void Set(Local<Value> key, Local<Value> value);
  Local<Value> Get(Local<Value> key);
  bool Has(Local<Value> key);
  bool Delete(Local<Value> key);
};

Value类是所有的JavaScript类型的基类,它有两个巨大的子类,一个是代表原始类型的Primitive,一个就是所有js对象之根Object类。当然,还有个实现上用途的包装C++ void*指针的External类。

ES6新增了Symbol类型,它和String共同派生自Name类型。
虽然JavaScript的数都是浮点数,但是在v8的实现里,整数仍然是大行其道的。

对象类型中,Promise, Proxy, ArrayBufferView这些都是ES6新增的。有些还需要--harmony选项的支持。
剩下是对于原始类型的包装,如NumberObject, BooleanObject, StringObject, SymbolObject。
还有的就是Date日期类和RegExp正则表达式类两个内置类的支持,也在API中。

最后,直接继承自Data的,还有4个类:

  • Private:私有的符号
  • Template:模板,分为函数模板和对象模板。这两个在后面我们分析v8的实现中有重要意义,因为它们是将JavaScript函数或者对象绑定到C++相应的对象上的重要工具。
  • 两个签名类,用于调用时检验用

小结

这一节新增的概念可能有点多。我们尝试小结一下:
v8提供了可供外部调用的一系列的API:

  • Isolate:提供了一个v8虚拟机的实例
  • Context:提供了一个沙箱,也就是独立的上下文可以运行Isolate
  • v8通过HandleScope栈式管理内存
  • Local<>是最常用的变量分配方式
  • v8的数据类型基类是Data,所有js对象的基类是Value。Value下面分Primitive和Object

最后引用一个新概念,v8为了提升性能,引入了一个高效的编译器Crankshaft.

好了,这一讲就这么多。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 《ilua》速成开发手册3.0 官方用户交流:iApp开发交流(1) 239547050iApp开发交流(2) 1...
    叶染柒丶阅读 10,494评论 0 11
  • 中英文原文链接:https://github.com/Chunlin-Li/Chunlin-Li.github.i...
    雲凌禹阅读 3,826评论 1 1
  • 2001年的夏天,我刚毕业,看着寝室里的同学都一个个陆续找到了工作单位,心头不免着慌,匆匆忙忙地与一家联系过的施工...
    李有犀阅读 404评论 4 5
  • 我不记得你去年春天的样子 不记得你的吻拂过我的心 不记得你温柔摩挲我的衣领 不记得你的笑溢满我的眼睛 我不记得你今...
    花花的樱花阅读 543评论 0 2
  • ①可以试着建立合成索引,context text所在表再加一列:hash_index varchar(50) 专门...
    Shaun_lan阅读 4,085评论 0 0