v8源码解析之Dictionary(v8 0.1.5)

Dictionary我们应该不陌生,他是HashTable的基类。我们看一下定义

// 字典基类,prefix大小为两个指针元素,数组中每个元素大小是3个指针
class DictionaryBase: public HashTable<2, 3> {};
class Dictionary: public DictionaryBase {}

由定义可以看到Dictionary的布局如下

Dictionary底层是数组实现的,每个元素的大小是三个指针(key、value、detail)。接着我们看一下类的具体实现。
1 value的存取

  // 一个元素是三个指针,第二个指针指向值,所以加一
  Object* ValueAt(int entry) { return get(EntryToIndex(entry)+1); }

  // 设置值
  void ValueAtPut(int entry, Object* value) {
    set(EntryToIndex(entry)+1, value);
  }

2 PropertyDetails存取
PropertyDetails是对属性细节的封装,比如是否可枚举,可读写。

 // 第三个指针是detail
  PropertyDetails DetailsAt(int entry) {
    return PropertyDetails(Smi::cast(get(EntryToIndex(entry) + 2)));
  }

  // 设置detail
  void DetailsAtPut(int entry, PropertyDetails value) {
    set(EntryToIndex(entry) + 2, value.AsSmi());
  }

3 RemoveNumberEntries
RemoveNumberEntries是删除key的值在from和to之间的元素。

void Dictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
  // Do nothing if the interval [from, to) is empty.
  if (from >= to) return;

  int removed_entries = 0;
  Object* sentinel = Heap::null_value();
  int capacity = Capacity();
  // 遍历所有元素
  for (int i = 0; i < capacity; i++) {
    // 找到元素的首地址,分为三个指针,分别是key、value、detail
    Object* key = KeyAt(i);
    if (key->IsNumber()) {
      uint32_t number = static_cast<uint32_t>(key->Number());
      // 命中则设置对应的索引的值,key、value、detail分别为null、null、0
      if (from <= number && number < to) {
        SetEntry(i, sentinel, sentinel, Smi::FromInt(0));
        // 记录被删除的元素个数
        removed_entries++;
      }
    }
  }
  // 重新设置已使用元素的个数
  SetNumberOfElements(NumberOfElements() - removed_entries);
}

4 RemoveHoles
RemoveHoles实现删除数组中的空元素,通过申请一个新的数组,然后把之前数据的有效值复制过去,完成无效元素的删除。

// 删除空的元素
Object* Dictionary::RemoveHoles() {
  int capacity = Capacity();
  // 当前已使用的元素个数,分配一个新的数组
  Object* obj = Allocate(NumberOfElements());
  if (obj->IsFailure()) return obj;
  Dictionary* dict = Dictionary::cast(obj);
  uint32_t pos = 0;
  for (int i = 0; i < capacity; i++) {
    Object* k = KeyAt(i);
    // 有效元素则追加到新的数组
    if (IsKey(k)) {
      dict->AddNumberEntry(pos++, ValueAt(i), DetailsAt(i));
    }
  }
  return dict;
}

5 CopyValuesTo
CopyValuesTo把对象中的有效值复制到另一个数组中

void Dictionary::CopyValuesTo(FixedArray* elements) {
  int pos = 0;
  int capacity = Capacity();
  for (int i = 0; i < capacity; i++) {
    Object* k = KeyAt(i);
    if (IsKey(k)) elements->set(pos++, ValueAt(i));
  }
  ASSERT(pos == elements->length());
}

6 DeleteProperty
DeleteProperty实现删除字典中的元素,如果可以的话。

Object* Dictionary::DeleteProperty(int entry) {
  // 获取该元素的属性信息
  PropertyDetails details = DetailsAt(entry);
  // 该元素是否可以删除
  if (details.IsDontDelete()) return Heap::false_value();
  // 可以删除则设置为无效元素
  SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0));
  // 已使用元素减一
  ElementRemoved();
  return Heap::true_value();
}

7 NumberOfElementsFilterAttributes
NumberOfElementsFilterAttributes找出不符合某属性的元素个数。

int Dictionary::NumberOfElementsFilterAttributes(PropertyAttributes filter) {
  int capacity = Capacity();
  int result = 0;
  for (int i = 0; i < capacity; i++) {
    Object* k = KeyAt(i);
    if (IsKey(k)) {
      PropertyAttributes attr = DetailsAt(i).attributes();
      if ((attr & filter) == 0) result++;
    }
  }
  return result;
}

比如找出可以枚举的元素个数。

int Dictionary::NumberOfEnumElements() {
  return NumberOfElementsFilterAttributes(
      static_cast<PropertyAttributes>(DONT_ENUM));
}

8 CopyKeysTo、CopyEnumKeysTo
CopyKeysTo、CopyEnumKeysTo实现复制符合条件的元素的key到另一个数组中。

void Dictionary::CopyKeysTo(FixedArray* storage, PropertyAttributes filter) {
  ASSERT(storage->length() >= NumberOfEnumElements());
  int capacity = Capacity();
  int index = 0;
  for (int i = 0; i < capacity; i++) {
     Object* k = KeyAt(i);
     if (IsKey(k)) {
       PropertyAttributes attr = DetailsAt(i).attributes();
       // 不符合filter的key复制到storage数组
       if ((attr & filter) == 0) storage->set(index++, k);
     }
  }
  ASSERT(storage->length() >= index);
}
// 复制可枚举元素的key和detail到新数组
void Dictionary::CopyEnumKeysTo(FixedArray* storage, FixedArray* sort_array) {
  ASSERT(storage->length() >= NumberOfEnumElements());
  int capacity = Capacity();
  int index = 0;
  for (int i = 0; i < capacity; i++) {
     Object* k = KeyAt(i);
     if (IsKey(k)) {
       PropertyDetails details = DetailsAt(i);
       // 可以枚举的元素
       if (!details.IsDontEnum()) {
         storage->set(index, k);
         sort_array->set(index, Smi::FromInt(details.index()));
         index++;
       }
     }
  }
  // 对sort_array的内容排序,storage的元素位置也发生相应的变化
  storage->SortPairs(sort_array);
  ASSERT(storage->length() >= index);
}

9 设置字典的内容
AtPut实现把键对值加入到字典,如果key已经存在则更新value。

Object* Dictionary::AtPut(Key* key, Object* value) {
  // 根据key判断是否可以找到对应的项
  int entry = FindEntry(key);

  // If the entry is present set the value;
  // 已经存在则更新值
  if (entry != -1) {
    ValueAtPut(entry, value);
    return this;
  }
  
  Object* obj = EnsureCapacity(1, key);
  if (obj->IsFailure()) return obj;
  // 拿到key
  Object* k = key->GetObject();
  if (k->IsFailure()) return k;
  PropertyDetails details = PropertyDetails(NONE, NORMAL);
  // 新增一个元素到字典
  Dictionary::cast(obj)->AddEntry(k, value, details, key->Hash());
  return obj;
}

我们接着看一下AddEntry。

void Dictionary::AddEntry(Object* key,
                          Object* value,
                          PropertyDetails details,
                          uint32_t hash) {
  // 找出插入的相对位置
  uint32_t entry = FindInsertionEntry(key, hash);
  // index记录元素枚举时的顺序,如果是0则赋值
  if (details.index() == 0 && key->IsString()) {
    // 获取下一个枚举序号
    int index = NextEnumerationIndex();
    details = PropertyDetails(details.attributes(), details.type(), index);
    // 更新下一个可用序号
    SetNextEnumerationIndex(index + 1);
  }
  // 真正设置内容
  SetEntry(entry, key, value, details);
  // 已使用元素加一
  ElementAdded();
}

我们再看一下SetEntry。

void Dictionary::SetEntry(int entry,
                          Object* key,
                          Object* value,
                          PropertyDetails details) {
  ASSERT(!key->IsString() || details.index() > 0);
  // 算出真正的存储位置
  int index = EntryToIndex(entry);
  WriteBarrierMode mode = GetWriteBarrierMode();
  // 设置key、value、detail
  set(index, key, mode);
  set(index+1, value, mode);
  fast_set(this, index+2, details.AsSmi());
}

10 GenerateNewEnumerationIndices
GenerateNewEnumerationIndices实现序号的重新调整,字典中每个元素都有一个序号。新增元素的时候,序号会自增,但是删除元素的时候,元素对应的id是无法回收重新使用的,因为序号是有序的,所以序号会耗尽,这时候就需要重新排序,以利用删除元素的那些序号。

Object* Dictionary::GenerateNewEnumerationIndices() {
  // 当前有效元素个数
  int length = NumberOfElements();

  // Allocate and initialize iteration order array.
  // 分配新的数组
  Object* obj = Heap::AllocateFixedArray(length);
  if (obj->IsFailure()) return obj;
  FixedArray* iteration_order = FixedArray::cast(obj);
  // 按序存储0....length - 1
  for (int i = 0; i < length; i++) iteration_order->set(i, Smi::FromInt(i));

  // Allocate array with enumeration order.
  // 再分配一个数组
  obj = Heap::AllocateFixedArray(length);
  if (obj->IsFailure()) return obj;
  FixedArray* enumeration_order = FixedArray::cast(obj);

  // Fill the enumeration order array with property details.
  int capacity = Capacity();
  int pos = 0;
  // 存储有效元素的序号
  for (int i = 0; i < capacity; i++) {
    if (IsKey(KeyAt(i))) {
      enumeration_order->set(pos++, Smi::FromInt(DetailsAt(i).index()));
    }
  }

  // Sort the arrays wrt. enumeration order.
  /*
    对enumeration_order进行排序,iteration_order元素的位置发生相应变化
    即按照枚举序号进行排序
    iteration_order = [0,1,2]
    enumeration_order = [5,6,4]
    排序后
    iteration_order = [2,0,1]
    enumeration_order = [4,5,6] 
    iteration_order记录了原来的位置,所以2记录了4原来所在的相对位置(因为可能有空洞,所以不一定是绝对问题),
    4现在是最小的,所以原来第二个位置的元素序号最小 
  */
  iteration_order->SortPairs(enumeration_order);

  // Overwrite the enumeration_order with the enumeration indices.
  for (int i = 0; i < length; i++) {
    // 获取元素之前的相对位置
    int index = Smi::cast(iteration_order->get(i))->value();
    // 重新生成序号
    int enum_index = PropertyDetails::kInitialIndex + i;
    // 更新对应位置的序号
    enumeration_order->set(index, Smi::FromInt(enum_index));
  }

  // Update the dictionary with new indices.
  capacity = Capacity();
  /*
    pos用于处理字典中元素的绝对位置到相对位置的映射,
    如[1, null, 3] 和[1,2],那么更新第三个元素的时候,得到的值是2
  */
  pos = 0;
  // 遍历每个元素,更新有效元素的枚举序号
  for (int i = 0; i < capacity; i++) {
    if (IsKey(KeyAt(i))) {
      int enum_index = Smi::cast(enumeration_order->get(pos++))->value();
      PropertyDetails details = DetailsAt(i);
      PropertyDetails new_details =
          PropertyDetails(details.attributes(), details.type(), enum_index);
      DetailsAtPut(i, new_details);
    }
  }

  // Set the next enumeration index.
  // 下一个可用的序号
  SetNextEnumerationIndex(PropertyDetails::kInitialIndex+length);
  return this;
}

11 Allocate
Allocate用于分类一个字典

Object* Dictionary::Allocate(int at_least_space_for) {
  Object* obj = DictionaryBase::Allocate(at_least_space_for);
  // Initialize the next enumeration index.
  if (!obj->IsFailure()) {
    // 初始化序号
    Dictionary::cast(obj)->
        SetNextEnumerationIndex(PropertyDetails::kInitialIndex);
  }
  return obj;
}

12 EnsureCapacity
EnsureCapacity用于扩容

Object* Dictionary::EnsureCapacity(int n, Key* key) {
  // 序号不够用了,需要重新排序,利用没有被回收的序号
  if (key->IsStringKey() &&
      !PropertyDetails::IsValidIndex(NextEnumerationIndex() + n)) {
    Object* result = GenerateNewEnumerationIndices();
    if (result->IsFailure()) return result;
  }
  // 调用父类的方法
  return DictionaryBase::EnsureCapacity(n, key);
}

13 UpdateMaxNumberKey
UpdateMaxNumberKey用于更新字典中数字key的最大值。如果得到阈值就进入slow模式。具体的在后面应用的时候分析

void Dictionary::UpdateMaxNumberKey(uint32_t key) {
  // 已经是slow模式,不需要处理了
  if (requires_slow_elements()) return;
  // 数字大于阈值,则设置slow模式
  if (key > kRequiresSlowElementsLimit) {
    set(kPrefixStartIndex, Smi::FromInt(kRequiresSlowElementsMask));
    return;
  }
  // 更新最大值
  Object* max_index_object = get(kPrefixStartIndex);
  if (!max_index_object->IsSmi() || max_number_key() < key) {
    set(kPrefixStartIndex, Smi::FromInt(key << kRequiresSlowElementsTagSize));
  }
}

bool Dictionary::requires_slow_elements() {
  Object* max_index_object = get(kPrefixStartIndex);
  if (!max_index_object->IsSmi()) return false;
  return 0 !=
      (Smi::cast(max_index_object)->value() & kRequiresSlowElementsMask);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值