V8 中 JavaScript 个对象的内存布局
Memory layout of JavaScript objects in V8
我正在寻找一些与 V8 的 C 绑定,因此我需要弄清楚各种原始 JavaScript 类型的内存布局。任何地方都有关于这些细节的文档吗?
由于您的问题是关于如何为 Node.js 编写 V8 C++ 插件,您应该只参考 nodeJs which has a reasonably simple guide for writing addons;
的手册
如果您的问题是关于如何创建后台线程,那么可以使用 nodejs 插件帮助您,但是 also read this 如何直接编写它。
您永远不要尝试直接访问 V8 的内存,因为内存和对象可能会移动——始终使用 API
您无需了解数据类型布局即可为 V8 编写 C 绑定。当您使用 V8 时,永远不会真正直接访问对象,而是通过 API - 只有 V8 实现知道它们是如何布局的。例如从一个对象 o
得到一个 属性 foo
在 C++ 中看起来像这样:
v8::Handle<v8::Object> o;
v8::Handle<v8::Object> v =
o->Get(v8::String::NewFromUtf8(isolate, "foo"));
现在要将其包装到 C 中,您 仅 需要知道如何表示和传递 v8::Handle<T>
,然后您可以像这样编写包装器:
template<typename T> Handle<T> FromC(v8_handle_t h) { /* ... */ }
template<typename T> v8_handle_t ToC(Handle<T> h) { /* ... */ }
extern "C" v8_handle_t v8_object_get(v8_handle_t self,
v8_handle_t key) {
return ToC(FromC<Object>(self)->Get(FromC<Value>(key)));
}
那里面有什么v8::Handle<T>
?实际上,它只是指向某个槽的指针,而槽又包含指向底层 V8 对象的实际指针。这种双重间接的存在是为了使 V8 GC 能够精确地跟踪 C++ 代码中正在使用的对象,并允许它移动这些对象。
所以理论上您可以将 v8_handle_t
定义为不透明指针并像这样编组它:
typedef struct v8_object_pointer_t* v8_handle_t; // Opaque pointer
static_assert(sizeof(v8_handle_t) == sizeof(Handle<Object>))
template<typename T> Handle<T> FromC(v8_handle_t h) {
return *(Handle<T>*)&h;
}
template<typename T> v8_handle_t ToC(const Handle<T>& h) {
return *(v8_handle_t*)&h;
}
管理名为 HandleScope
的结构会管理 Handle
的结构带来了一点麻烦。在 C++ API 中,它依赖于 RAII 模式来管理一些后备存储。最简单的处理方法可能是:
typedef struct {
void* a[3];
} v8_handle_scope_t;
static_assert(sizeof(v8_handle_scope_t) == sizeof(HandleScope))
void v8_handle_scope_enter(v8_handle_scope_t* scope) {
new(scope) HandleScope;
}
void v8_handle_scope_leave(v8_handle_scope_t* scope) {
delete (HandleScope*)scope;
}
在需要处理范围的代码中仔细平衡使用:
for (i = 0; i < N; i++) {
v8_handle_scope_t scope;
v8_handle_scope_enter(&scope);
// action
v8_handle_scope_leave(&scope);
}
我正在寻找一些与 V8 的 C 绑定,因此我需要弄清楚各种原始 JavaScript 类型的内存布局。任何地方都有关于这些细节的文档吗?
由于您的问题是关于如何为 Node.js 编写 V8 C++ 插件,您应该只参考 nodeJs which has a reasonably simple guide for writing addons;
的手册如果您的问题是关于如何创建后台线程,那么可以使用 nodejs 插件帮助您,但是 also read this 如何直接编写它。
您永远不要尝试直接访问 V8 的内存,因为内存和对象可能会移动——始终使用 API
您无需了解数据类型布局即可为 V8 编写 C 绑定。当您使用 V8 时,永远不会真正直接访问对象,而是通过 API - 只有 V8 实现知道它们是如何布局的。例如从一个对象 o
得到一个 属性 foo
在 C++ 中看起来像这样:
v8::Handle<v8::Object> o;
v8::Handle<v8::Object> v =
o->Get(v8::String::NewFromUtf8(isolate, "foo"));
现在要将其包装到 C 中,您 仅 需要知道如何表示和传递 v8::Handle<T>
,然后您可以像这样编写包装器:
template<typename T> Handle<T> FromC(v8_handle_t h) { /* ... */ }
template<typename T> v8_handle_t ToC(Handle<T> h) { /* ... */ }
extern "C" v8_handle_t v8_object_get(v8_handle_t self,
v8_handle_t key) {
return ToC(FromC<Object>(self)->Get(FromC<Value>(key)));
}
那里面有什么v8::Handle<T>
?实际上,它只是指向某个槽的指针,而槽又包含指向底层 V8 对象的实际指针。这种双重间接的存在是为了使 V8 GC 能够精确地跟踪 C++ 代码中正在使用的对象,并允许它移动这些对象。
所以理论上您可以将 v8_handle_t
定义为不透明指针并像这样编组它:
typedef struct v8_object_pointer_t* v8_handle_t; // Opaque pointer
static_assert(sizeof(v8_handle_t) == sizeof(Handle<Object>))
template<typename T> Handle<T> FromC(v8_handle_t h) {
return *(Handle<T>*)&h;
}
template<typename T> v8_handle_t ToC(const Handle<T>& h) {
return *(v8_handle_t*)&h;
}
管理名为 HandleScope
的结构会管理 Handle
的结构带来了一点麻烦。在 C++ API 中,它依赖于 RAII 模式来管理一些后备存储。最简单的处理方法可能是:
typedef struct {
void* a[3];
} v8_handle_scope_t;
static_assert(sizeof(v8_handle_scope_t) == sizeof(HandleScope))
void v8_handle_scope_enter(v8_handle_scope_t* scope) {
new(scope) HandleScope;
}
void v8_handle_scope_leave(v8_handle_scope_t* scope) {
delete (HandleScope*)scope;
}
在需要处理范围的代码中仔细平衡使用:
for (i = 0; i < N; i++) {
v8_handle_scope_t scope;
v8_handle_scope_enter(&scope);
// action
v8_handle_scope_leave(&scope);
}