这篇文章主要讲解了“nodejs源码分析中c++层的通用逻辑是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“nodejs源码分析中c++层的通用逻辑是什么”吧!
在肃南裕固族自治等地区,都构建了全面的区域性战略布局,加强发展的系统性、市场前瞻性、产品创新能力,以专注、极致的服务理念,为客户提供成都做网站、成都网站设计 网站设计制作定制设计,公司网站建设,企业网站建设,成都品牌网站建设,成都全网营销推广,外贸网站制作,肃南裕固族自治网站建设费用合理。
我们知道nodejs分为js、c++、c三层,本文以tcp_wrap.cc为例子分析c++层实现的一些通用逻辑。nodejs的js和c++通信原理q.com/s?__biz=MzUyNDE2OTAwNw==&mid=2247484815&idx=1&sn=525d9909c35eabf3c728b303d27061df&chksm=fa303fcfcd47b6d9604298d0996414a5e16c798c1a2dab4e01989bb41ba9c5372ebc00ca0943&token=162783191&lang=zh_CN#rd)之前已经分析过,所以直接从tcp模块导出的功能开始分析(Initialize函数)。
void TCPWrap::Initialize(Local
这里只摘取了部分的代码 ,因为我们只关注原理,这里分别涉及到函数模板对象模板和函数原型等内容。上面的代码以js来表示如下:
function TCP() {
this.reading = false;
// 对应SetInternalFieldCount(1)
this.point = null;
// 对应env->NewFunctionTemplate(New);
New({
Holder: this,
This: this,
returnValue: {},
...
});
}
TCP.prototype.bind = Bind;
TCP.prototype.connect = Connect;
通过上面的定义,完成了c++模块功能的导出,借助nodejs的机制,我们就可以在js层调用TCP函数。
const { TCP, constants: TCPConstants } = process.binding('tcp_wrap');
const instance = new TCP(...);
instance.bind(...);
我们先分析执行new TCP()的逻辑,然后再分析bind的逻辑,因为这两个逻辑涉及的机制是其他c++模块也会使用到的。因为TCP对应的函数是Initialize函数里的t->GetFunction()对应的值。所以new TCP()的时候,v8首先会创建一个c++对象(内容由Initialize函数里定义的那些,也就是文章开头的那段代码的定义)。然后执行回调New函数。
// 执行new TCP时执行
void TCPWrap::New(const FunctionCallbackInfo& args) {
// 是否以构造函数的方式执行,即new TCP
CHECK(args.IsConstructCall());
CHECK(args[0]->IsInt32());
Environment* env = Environment::GetCurrent(args);
// 忽略一些不重要的逻辑
/*
args.This()为v8提供的一个c++对象(由Initialize函数定义的模块创建的)
调用该c++对象的SetAlignedPointerInInternalField(0,this)关联this(new TCPWrap()),
见HandleWrap
*/
new TCPWrap(env, args.This(), provider);
}
我们看到New函数的逻辑很简单。直接调用new TCPWrap,其中第二个入参args.This()就是由Initialize函数定义的函数模板创建出来的对象。我们继续看new TCPWrap()。
TCPWrap::TCPWrap(Environment* env,
Local
构造函数只有一句代码,该代码是初始化一个结构体,我们可以不关注,我们需要关注的是父类ConnectionWrap的逻辑。
template
ConnectionWrap::ConnectionWrap(Environment* env,
Local
我们发现ConnectionWrap也没有什么逻辑,继续看LibuvStreamWrap。
LibuvStreamWrap::LibuvStreamWrap(Environment* env,
Local
继续做一些初始化,我们只关注HandleWrap
HandleWrap::HandleWrap(Environment* env,
Local
重点来了,就是Wrap函数。
template
void Wrap(v8::Local object, TypeName* pointer) {
object->SetAlignedPointerInInternalField(0, pointer);
}
void v8::Object::SetAlignedPointerInInternalField(int index, void* value) {
i::Handle obj = Utils::OpenHandle(this);
i::Handle::cast(obj)->SetEmbedderField(
index, EncodeAlignedAsSmi(value, location));
}
void JSObject::SetEmbedderField(int index, Smi* value) {
// GetHeaderSize为对象固定布局的大小,kPointerSize * index为拓展的内存大小,根据索引找到对应位置
int offset = GetHeaderSize() + (kPointerSize * index);
// 写对应位置的内存,即保存对应的内容到内存
WRITE_FIELD(this, offset, value);
}
Wrap函数展开后,做的事情就是把一个值保存到v8 c++对象的内存里。那保存的这个值是啥呢?我们看Wrap函数的入参Wrap(object, this)。object是由函数模板创建的对象,this是一个TCPWrap对象。所以Wrap函数做的事情就是把一个TCPWrap对象保存到一个函数模板创建的对象里。这有啥用呢?我们继续分析。这时候new TCP就执行完毕了。我们看看这时候执行new TCP().bind()函数的逻辑。
void TCPWrap::Bind(const FunctionCallbackInfo& args) {
TCPWrap* wrap;
// 解包处理
ASSIGN_OR_RETURN_UNWRAP(&wrap,
args.Holder(),
args.GetReturnValue().Set(UV_EBADF));
node::Utf8Value ip_address(args.GetIsolate(), args[0]);
int port = args[1]->Int32Value();
sockaddr_in addr;
int err = uv_ip4_addr(*ip_address, port, &addr);
if (err == 0) {
err = uv_tcp_bind(&wrap->handle_,
reinterpret_cast(&addr),
0);
}
args.GetReturnValue().Set(err);
}
我们只需关系ASSIGN_OR_RETURN_UNWRAP宏的逻辑。其中args.Holder()表示Bind函数的属主,根据前面的分析我们知道属主是Initialize函数定义的函数模板创建出来的对象。这个对象保存了一个TCPWrap对象。我们展开ASSIGN_OR_RETURN_UNWRAP看看。
#define ASSIGN_OR_RETURN_UNWRAP(ptr, obj, ...) \
do { \
*ptr = \
Unwrap::type>(obj); \
if (*ptr == nullptr) \
return __VA_ARGS__; \
} while (0)
template
TypeName* Unwrap(v8::Local object) {
// 把调用SetAlignedPointerFromInternalField设置的值取出来
void* pointer = object->GetAlignedPointerFromInternalField(0);
return static_cast(pointer);
}
展开后我们看到,主要的逻辑是把在c++对象中保存的那个TCPWrap对象取出来。然后就可以使用TCPWrap对象了。
感谢各位的阅读,以上就是“nodejs源码分析中c++层的通用逻辑是什么”的内容了,经过本文的学习后,相信大家对nodejs源码分析中c++层的通用逻辑是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!