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);
}