V8嵌入开发(四)

Context 上下文

Context上下文有两个重要职责:

代表一个沙箱

包含了当前执行环境的全局函数对象,即内置对象和函数。

简单的说,一个Window对象对应于一个Context。例如<iframe>parent frame的有不同的Window对象,所以不同的frame具有不同的Context。由于每个Context创建了自己的全局变量和作用域,因此<iframe>的全局变量和原型链与parent frame的全局变量和原型链是隔离的

Context 和 Isolate 的关系

isolate和Context之间的关系是比较有趣的。一个isolate会在多个frame中执行JavaScripts,每个frame都有自己的context。这个意思就是一个isolate下的Context是会变化的。换句话说,isolate和Context之间的关系是1:N

单独的Isolate是不足以运行脚本的,我们在此需要一个全局对象。Context就是提供此全局变量的工具。它在其所处的Isolate管理的heap中建立一个对象,并以此为全局变量构建出一个完成的执行环境供我们的脚本使用。

因此,对于一个给定的Isolate, 不仅其可以有多个Context,并且这些Context之间可以共享某些对象。 Entered context and current context 这里我们有一个Entered context和current context的概念。要了解差异,您需要了解两种运行时堆栈:第一个堆栈是JavaScript函数堆栈。该堆栈由V8管理。当一个函数调用另一个函数时,被调用函数入堆栈。当该函数返回时,该函数从堆栈出栈,然后返回到现在位于堆栈顶部的调用函数。每个函数都有一个相关的Context,我们将当前正在运行的函数的上下文(即,堆栈顶部的函数的上下文)称为current context。

看以下例子:

// main.html
<html><body>
<iframe src="iframe.html"></iframe>
<script>
var iframe = document.querySelector("iframe");
iframe.onload = function () {
    iframe.contentWindow.func();
}
</script>
</body></html>

// iframe.html
<script>
function func() {
  ...;
}
</script>

在上面的示例中,在运行func()时,current context指的是<iframe>的context。

第二个堆栈以更粗糙的粒度运行。该堆栈由V8绑定(而不是V8)管理。当V8绑定调用JavaScript时,V8绑定进入context并将context推送到堆栈。 JavaScript开始在context中运行。当JavaScript完成并且控件返回到V8绑定时,V8绑定会从堆栈中弹出上下文。鉴于V8绑定和V8之间的控制可以嵌套(即,V8绑定调用JavaScript,调用V8绑定,调用另一个JavaScript等),这些context形成堆栈。推送和弹出是由任何V8 API完成的,它采用上下文参数或显式调用v8 :: Context :: Enter()和v8 :: Context :: Exit()。我们将最近输入的context称为Entered context。

在上面的示例中,在运行func()时,Entered context是main frame的context(而不是<iframe>的context)。 Entered context是实现HTML规范的条目设置对象的概念。当前上下文是实现HTML规范的现任设置对象的概念。

总之,Entered context是从中开始当前JavaScript执行的context。current context是当前正在运行的JavaScript函数的context。

还有另一个称为调试器上下文的特殊上下文。如果调试器处于活动状态,则可以将调试器上下文插入到上下文堆栈中。

查阅相关资料,类似nodejs这种环境,只有一个Context。 目前可以推断的是,类似微信小游戏中的“子域”,则是使用了第二个Context,用以隔离JS脚本。

创建一个Context

Local<Context> context = Context::New(isolate);
static Local<Context> v8::Context::New(Isolate * isolate,
    ExtensionConfiguration * extensions = nullptr,
    MaybeLocal< ObjectTemplate > global_template = MaybeLocal< ObjectTemplate >(),
    MaybeLocal< Value > global_object = MaybeLocal< Value >(),
    DeserializeInternalFieldsCallback internal_fields_deserializer = DeserializeInternalFieldsCallback(),
    MicrotaskQueue * microtask_queue = nullptr 
    )

参数解释

根据一个Context快照来创建一个新的Context

static MaybeLocal<Context> v8::Context::FromSnapshot(Isolate * isolate,
    size_t context_snapshot_index,
    DeserializeInternalFieldsCallback embedder_fields_deserializer = DeserializeInternalFieldsCallback(),
    ExtensionConfiguration * extensions = nullptr,
    MaybeLocal< Value > global_object = MaybeLocal< Value >(),
    MicrotaskQueue * microtask_queue = nullptr 
    )

从(非默认)上下文快照创建新上下文。无法提供全局对象模板,因为我们不从模板创建新的全局对象,但可以重用全局对象。 context_snapshot_index:要从中反序列化的上下文快照的索引。使用v8::Context::New作为默认快照。

Context::Scope

和 v8::Isolate::Scope 一样,内部只是调用了 Context 的 Enter 方法和 Exit 方法,负责执行和退出。