了解Google的V8 C ++代码库中的继承

时间:2015-07-19 22:00:11

标签: c++ inheritance architecture v8

我无法理解Google V8 JavaScript引擎中继承的实现。它显然(?)实现了一个继承层次结构,但似乎完全取消了虚函数。

这是继承层次结构,详见objects.h header file

// Inheritance hierarchy:
// - Object
//   - Smi          (immediate small integer)
//   - HeapObject   (superclass for everything allocated in the heap)
//     - JSReceiver  (suitable for property access)
//       - JSObject
//         - JSArray
// ... and many more entries

大多数对象类型派生自Object,声明如下:

// Object is the abstract superclass for all classes in the
// object hierarchy.
// Object does not use any virtual functions to avoid the
// allocation of the C++ vtable.
// Since both Smi and HeapObject are subclasses of Object no
// data members can be present in Object.
class Object {
// ... bunch of method declarations and definitions
};

接下来宣布相对简单的Smi类:

class Smi: public Object {
 public:
 // methods declarations and static member definitions
};

等等。

对于我的生活,我无法理解,Smi的实例怎么可以用作Object;有没有虚拟功能我在the implementation file, objects.cc 中找不到覆盖。但是,在17,290行,试图了解正在发生的事情是一项艰巨的任务。

另一个困难是,我在同一个头文件中找到了一个ObjectVisitor类(这个类更多是经典;它由虚方法组成)。但我在Accept(Visitor*)基类中找不到等效的Object(或类似)方法。

我具体要求的是一个最小的例子,说明了这种继承模式是如何工作的。

2 个答案:

答案 0 :(得分:3)

objects.h中的类实际上并没有定义真正的C ++类。他们没有任何领域。这些类只是在V8 JavaScript堆上管理的对象的外观。因此,他们也不能拥有任何虚函数,因为这需要将vtable指针放入JS堆中。相反,所有调度都是通过显式类型检查和向下转换手动完成的。

方法内的this指针也不实。对于smis,this只是一个整数。对于其他一切,它是指向V8堆的指针,由一个用于标记。任何实际的访问器方法都会屏蔽此指针并添加一个偏移量来访问堆中的相应地址。每个字段的偏移量也在类中手动定义。

答案 1 :(得分:2)

请查看Object::IsPromise(),了解其工作原理的完美示例:

bool Object::IsPromise(Handle<Object> object) {
  if (!object->IsJSObject()) return false;
  auto js_object = Handle<JSObject>::cast(object);
  // Promises can't have access checks.
  if (js_object->map()->is_access_check_needed()) return false;
  auto isolate = js_object->GetIsolate();
  // TODO(dcarney): this should just be read from the symbol registry so as not
  // to be context dependent.
  auto key = isolate->promise_status();
  // Shouldn't be possible to throw here.
  return JSObject::HasRealNamedProperty(js_object, key).FromJust();
}

这里使用的继承方式是静态的。也就是说,类型查询由代理或容器完成(使用一些隐藏的魔法,一眼就看起来他们使用引用来查询标记),以及从Object到派生类的转换由static_cast<>()完成。这样,可以调用派生类的成员函数。

请注意,在上面的函数中,类型查询和强制类型由Handle<>类间接执行,而不是由Object或其任何派生类执行。

另请注意,接受 ObjectVisitor作为参数的函数统称为Iterate,并且这些函数都出现在代理或句柄上。