V8里又有一大波即将废弃的API

Posted on July 22, 2016

相关链接:https://groups.google.com/forum/#!topic/v8-users/gQVpp1HmbqM

这次API变动涉及的规模非常之庞大,绝大部分我们会使用到的接口都要更新一遍。这里简单解读一下变动的原因以及新接口的使用方法。首先来看下面这个示例:

Local<Value> x = some_value;
Local<String> s = x.ToString(); // 方法内部如果发生异常会返回空Handle
s->Anything(); // 访问空Handle导致崩溃!

这是一段非常常见的代码,我们有个类型是 Local<Value> 的变量 x,现在需要对它调用 toString() 方法来获取字符串。如果 toString() 方法因为栈内存不足或者其他任何原因发生异常,这时候返回的 Local<String> 就会是一个空Handle,而接着访问空Handle就会导致程序崩溃。正确的写法应该是这样:

if(!s.IsEmpty()){
    s->Anything(); // 判断不是空Handle才执行后续代码
}

使用Local Handle前我们需要加一个IsEmpty()的判断,阻止对空Handle的访问才可以避免程序崩溃。问题就出在这个API Style 上,V8现有的API没能有效的提醒开发者哪些返回值是需要检查 IsEmpty() 的。由于返回都是Local Handle,再加上开发者自己的函数也传递的各种 Local Handle,如果所有 Local Handle 使用前都检查一次太不现实了。所以实际情况是大家有的检查有的不检查,大大增加了整个系统崩溃的概率。

为了解决这个问题,V8引入了一组新的接口:MaybeMaybeLocal

旧API里类似这样的函数:

double NumberValue();
Local<String> ToString();

都会变成这样:

Maybe<double> NumberValue(Local<Context>);
MaybeLocal<String> ToString(Local<Context>);

总结就是:凡是可能返回空Handle的API,都改成了返回 Maybe Handle,而不是 Local Handle。MaybeLocal的意思就是可能有值,也可能是空的。将 Maybe Handle 转换为 Local Handle 使用如下方法:

MaybeLocal<Value> x = some_value;
if(x.IsEmpty()){
    Local<Value> s = x.ToLocalChecked();
}

注意这里如果不判断 IsEmpty(),如果 x 确实是空的,ToLocalChecked() 方法就会强制抛出一个异常。只有你非常肯定 x 确定不是空的,才可以省略 IsEmpty()

可以看出这套新API可以有效提醒开发者对空的 Handle 进行检查,因为你必须把 Maybe Handle 转换为 Local Handle 才可以使用,转换的过程再加上 IsEmpty() 的判断,可以从机制上避免写出不稳定的系统。到目前为止,V8里绝大部分API已经都改成了返回 Maybe Handle 的形式。根据原文的描述,等全部改完,会有六周的共存时间,然后直接返回 Local Handle 的旧API会全部移除。所以大家从现在开始全都使用返回 Maybe Handle 的版本吧。

这里最后提一下为何返回 Maybe Handle 的版本都多了一个 Context 参数?这里主要是因为要重载旧API,只有返回值不同在C++里不算合法重载,所以加了个Context参数,哭。不过在全局声明一个 Context 用就行,也不是什么难事。