前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >js引擎v8源码分析之Handle(基于v8 0.1.5)

js引擎v8源码分析之Handle(基于v8 0.1.5)

作者头像
theanarkh
发布2020-02-25 15:15:23
9770
发布2020-02-25 15:15:23
举报
文章被收录于专栏:原创分享原创分享

Handle是使用v8的时候很重要的一个概念和类。他本质是堆对象的封装。我们通过Handle管理真正的对象,而不是直接操作对象。Handle在v8中有两个实现。一个是对外使用的一个是内部使用的。我们先看一下内部使用的。

1 内部handle

代码语言:javascript
复制
template<class T>
class Handle {
 public:
  INLINE(Handle(T** location))  { location_ = location; }
  INLINE(explicit Handle(T* obj));

  INLINE(Handle()) : location_(NULL) {}

  template <class S> Handle(Handle<S> handle) {
    location_ = reinterpret_cast<T**>(handle.location());
  }

  INLINE(T* operator ->() const)  { return operator*(); }

  bool is_identical_to(const Handle<T> other) const {
    return operator*() == *other;
  }

  INLINE(T* operator*() const);

  T** location() const {
    return location_;
  }

  template <class S> static Handle<T> cast(Handle<S> that) {
    T::cast(*that);
    return Handle<T>(reinterpret_cast<T**>(that.location()));
  }

  static Handle<T> null() { return Handle<T>(); }
  bool is_null() {return location_ == NULL; }

  inline Handle<T> EscapeFrom(HandleScope* scope);

 private:
  T** location_;
};

下面是实现。

代码语言:javascript
复制
template<class T>
Handle<T>::Handle(T* obj) {
  location_ = reinterpret_cast<T**>(HandleScope::CreateHandle(obj));
}


template <class T>
inline T* Handle<T>::operator*() const {
  return *location_;
}

Handle类的定义没有太多的逻辑,就是对用户定义的对象指针进行封装。有一个重要的地址是构造函数。我们看到当我们定义一个Handle的时候,他会调HandleScope::CreateHandle生成一个Handle对象。在HandleScope那篇文章已经分析过了。handle对象的location对象指针一个内存,该内存保存了obj的地址。

2 外部handle

代码语言:javascript
复制
// T表示handle管理的对象的类型
template <class T> class Handle {
 public:

  Handle();

  explicit Handle(T* val) : val_(val) { }

  // *that得到指向handle管理的对象的指针,转成T类型,赋值给val_
  template <class S> inline Handle(Handle<S> that)
      : val_(reinterpret_cast<T*>(*that)) {

    TYPE_CHECK(T, S);
  }

  bool IsEmpty() { return val_ == 0; }

  T* operator->();

  T* operator*();

  void Clear() { this->val_ = 0; }

  /*
    比较handle指向的对象的地址是否相等
    this是指向当前对象的指针,*this是当前对象,**this是返回val_的值,看重载运算符*的实现
    *that是val_的值
  */
  template <class S> bool operator==(Handle<S> that) {
    void** a = reinterpret_cast<void**>(**this);
    void** b = reinterpret_cast<void**>(*that);
    // a等于0,则返回b是否等于0,是的话说明a==b,即true
    if (a == 0) return b == 0;
    // a不等于0,如果b==0,则返回false
    if (b == 0) return false;
    // 比较ab,即取val_里的内容比较
    return *a == *b;
  }

  template <class S> bool operator!=(Handle<S> that) {
    return !operator==(that);
  }

  template <class S> static inline Handle<T> Cast(Handle<S> that) {
    // 返回一个空的handle,即val_是null
    if (that.IsEmpty()) return Handle<T>();
    // *that得到指向handle管理的对象的指针,转成T类型的对象,转成底层对象是类型T的handle
    return Handle<T>(T::Cast(*that));
  }

 private:
  T* val_;
};

下面是实现。只有两个运算符的重载。

代码语言:javascript
复制
template <class T>
T* Handle<T>::operator->() {
  return val_;
}


template <class T>
T* Handle<T>::operator*() {
  return val_;
}

我们看到Handle的实现没有太内容,就是在对象和用户之前加了一层。下面看看他的两个子类Local和Persistent。

3 Local

1 Local类是基于栈分配的一种Handle,他在一个函数开始的时候,声明一个HandleScope,HandleScope下面所有的Handle都在最近的的HandleScope中分配,函数执行完后,会一起被释放。

代码语言:javascript
复制
template <class T> class Local : public Handle<T> {
 public:
  Local();
  // 调用Local函数的时候S被替换成that对应的类型,结果是Handle底层的val_指向一个T类型的对象
  template <class S> inline Local(Local<S> that)
      // *that即取得他底层对象的地址
      : Handle<T>(reinterpret_cast<T*>(*that)) {
    TYPE_CHECK(T, S);
  }
  template <class S> inline Local(S* that) : Handle<T>(that) { }
  template <class S> static inline Local<T> Cast(Local<S> that) {
    if (that.IsEmpty()) return Local<T>();
    return Local<T>(T::Cast(*that));
  }

  static Local<T> New(Handle<T> that);
};

Local没有做什么事情,是对基类Handle的简单继承。下面是实现。

代码语言:javascript
复制
template <class T>
Handle<T>::Handle() : val_(0) { }

template <class T>
Local<T>::Local() : Handle<T>() { }

template <class T>
Local<T> Local<T>::New(Handle<T> that) {
  if (that.IsEmpty()) return Local<T>();
  void** p = reinterpret_cast<void**>(*that);
  return Local<T>(reinterpret_cast<T*>(HandleScope::CreateHandle(*p)));
}

我们看看如果使用一个句柄。

代码语言:javascript
复制
HandleScope scope;
Local<String> source = String::New('hello');

我们看一下String::New的实现。

代码语言:javascript
复制
// i::Handle表示内部使用的handle
Local<String> v8::String::New(const char* data, int length) {
  if (length == -1) length = strlen(data);
  // 申请一个对象,由handle管理
  i::Handle<i::String> result = i::Factory::NewStringFromUtf8(i::Vector<const char>(data, length));
  return Utils::ToLocal(result);
}

Local<v8::String> Utils::ToLocal(v8::internal::Handle<v8::internal::String> obj) { 
    return Local<String>(reinterpret_cast<String*>(obj.location())); 
}

我们在看下HandleScope中的那个图。再来例假ToLocal函数的逻辑。

在这里插入图片描述 我们对着图来理解ToLocal,我们知道obj.location()返回的是指针,指向保存了对象地址的内存地址。然后转成String*,即拿到对象的地址。构造一个Local对象返回。即Local内部管理用户定义的对象(String::New函数执行完后,他里面定义的result,即handle被析构)。如下图。

在这里插入图片描述 当HandleScope析构的时候,他会释放用户定义的对象的内存,然后Local对象本身是在栈上分配的,也会被析构。这就是v8用本地handle(临时handle)管理堆对象的大致原理。一般来说handle在函数结束后就会被释放,如果想在函数执行完还使得句柄可用,可用使用逃逸(escape)。原理是销毁当前的HandleScope,然后在前一个HandleScope对象里分配一个handle。下面继续看看持久句柄。 4 Persisten

代码语言:javascript
复制
template <class T> class Persistent : public Handle<T> {
 public:

  Persistent();

  template <class S> inline Persistent(Persistent<S> that): Handle<T>(reinterpret_cast<T*>(*that)) {
    TYPE_CHECK(T, S);
  }

  template <class S> inline Persistent(S* that) : Handle<T>(that) { }

  template <class S> explicit inline Persistent(Handle<S> that)
      : Handle<T>(*that) { }

  template <class S> static inline Persistent<T> Cast(Persistent<S> that)         {
    if (that.IsEmpty()) return Persistent<T>();
    return Persistent<T>(T::Cast(*that));
  }

  static Persistent<T> New(Handle<T> that);

  void Dispose();

  void MakeWeak(void* parameters, WeakReferenceCallback callback);
  void ClearWeak();
  bool IsNearDeath();
  bool IsWeak();

 private:
  friend class ImplementationUtilities;
  friend class ObjectTemplate;
};

相对于基类Handle,Persistent多了几个功能,我们看一下使用用例。

代码语言:javascript
复制
Persistent<Context> context = Context::New();

我们看一下Context::New()的定义。

代码语言:javascript
复制
Persistent<Context> v8::Context::New(v8::ExtensionConfiguration* extensions,
                                     v8::Handle<ObjectTemplate> global_template,
                                     v8::Handle<Value> global_object) {

  i::Handle<i::Context> env = i::Bootstrapper::CreateEnvironment(
      Utils::OpenHandle(*global_object),
      global_template, extensions
  );

  return Persistent<Context>(Utils::ToLocal(env));
}

我们看一下CreateEnvironment的实现。

代码语言:javascript
复制
Handle<Context> Bootstrapper::CreateEnvironment(...参数) {
  Genesis genesis(global_object, global_template, extensions);
  return genesis.result();
}

Genesis::Genesis(...参数) {
  CreateRoots(global_template, global_object);
  result_ = global_context_;
 }

void Genesis::CreateRoots(...参数) {
  // 创建一个全局上下文对象,分配一个Context对象
  global_context_ =
      Handle<Context>::cast(
          GlobalHandles::Create(*Factory::NewGlobalContext()));
}

Handle<Context> result() { return result_; }

通过上面的代码我们知道Persistent指向的是一个GlobalHandles::Create返回的地址。所以我们主要分析GlobalHandles这个类的实现。这个类的代码比较多,我们只分析相关的(Node类维护一个对象的信息,地址,状态)。后面会单独分析。

代码语言:javascript
复制
// 一个handle对应一个Node
Handle<Object> GlobalHandles::Create(Object* value) {
  Counters::global_handles.Increment();
  Node* result;
  /*
    有一个free_list,保存着DESTROYED状态但还没有被释放的Node,
    first_free指向第一个节点,为NULL说明没有待回收的节点,即没有可重用的节点 
  */
  if (first_free() == NULL) {
    // Allocate a new node.
    // 没有可重用的节点则分配一个新的
    result = new Node(value);
    // 头插法,设置新增的node的下一个节点是当前头结点
    result->set_next(head());
    // 头指针指向新增的node
    set_head(result);
  } else {
    // Take the first node in the free list.
    // 获取一个可以重用的节点
    result = first_free();
    // 获取重用节点在free_list中的第一个节点,first_free指向新的可重用节点 
    set_first_free(result->next_free());
    // 重新初始化该节点
    result->Initialize(value);
  }
  // 返回一个handle对象
  return result->handle();
}

从上面的代码中我们大概知道,有一个链表,每个node节点保存了一个持久对象的信息。持久句柄指向的对象都是在这个链表里管理的。持久句柄最后要调Dispose释放。

代码语言:javascript
复制
template <class T>
void Persistent<T>::Dispose() {
  if (this->IsEmpty()) return;
  V8::DisposeGlobal(reinterpret_cast<void**>(**this));
}

void V8::DisposeGlobal(void** obj) {
  LOG_API("DisposeGlobal");
  if (has_shut_down) return;
  i::GlobalHandles::Destroy(reinterpret_cast<i::Object**>(obj));
}

// 销毁一个节点
void GlobalHandles::Destroy(Object** location) {
  Counters::global_handles.Decrement();
  if (location == NULL) return;
  Node* node = Node::FromLocation(location);
  node->Destroy();
  // Link the destroyed.
  // 设置待销毁节点在free_list链表里的下一个节点是当前的头结点
  node->set_next_free(first_free());
  // 头指针指向待销毁的节点,
  set_first_free(node);
}

大致是根据对象的地址转成node节点,销毁该节点。从链表中删除。

总结,这就是v8中关于handle的一些知识。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 内部handle
  • 2 外部handle
  • 3 Local
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档