V8中JavaScript对象的内存布局

15

我想编写一些与V8相关的C绑定,因此需要弄清各种基本JavaScript类型的内存布局。是否有关于这些细节的文档?


你现在是在询问对象还是原始值? - Bergi
4
你是在询问是否需要进行Node.js-C绑定或直接开发v8吗?这两者都有不同的文档位置,而且你都不需要直接访问内存,而是使用API。请注意,无论哪种情况下都不要直接访问内存。 - Soren
(最终意图是将其与Node一起使用) - Steve Klabnik
Node.js手册中有一些信息--请参阅此处以获取更多信息--https://dev59.com/Cmgu5IYBdhLWcg3w0qRK#22946062 - Soren
@soren,那都是C++。我需要C。 - Steve Klabnik
显示剩余2条评论
2个回答

5
你不需要了解数据类型的布局就能为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语言,您只需要知道如何表示和传递>,然后就可以编写包装器,例如:
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 垃圾回收器能够精确跟踪 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);
}

非常棒,感謝你的幫助。對於我的使用情況,我主要只關心立即複製數據而不必擔心保持對象活著,這是因為我擔心GC rooting和其他你提到的問題。 - Steve Klabnik
@SteveKlabnik 嗯,V8 API 明确地设计成这样是为了防止任何关于 rooting 的担忧 - 这就是为什么所有对象始终都是 rooted 的(除非你违反 HandleScope 嵌套并传递无效的 handles)。 - Vyacheslav Egorov

1

我的问题是关于如何编写C扩展,而不是C ++。 - Steve Klabnik
1
你需要将你的 C 代码用 C++ 包装起来。 - Soren
2
是的。为了做到这一点,我需要知道结构体的内存布局等信息,因此提出了这个问题。 - Steve Klabnik
1
正确。这就是为什么我需要了解这些细节,以便我知道如何将那些C结构转换为V8使用的正确结构。 - Steve Klabnik
1
请记住,V8 API 的更改频率相当高。因此,即使您能够创建映射到 V8 内部好东西的 C 结构体,它们也可能会在发布之间发生变化。我猜node-ffi不能帮助,但是...还是传递一下吧。还有nan,它试图为 Node.js 开发人员抽象出不断变化的 V8 API。 - Patrick Mueller
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接