v8源码解析之Dictionary(v8 0.1.5)

2024-03-27 21:18
文章标签 源码 解析 0.1 v8 dictionary

本文主要是介绍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是对属性细节的封装,比如是否可枚举,可读写。

 // 第三个指针是detailPropertyDetails DetailsAt(int entry) {return PropertyDetails(Smi::cast(get(EntryToIndex(entry) + 2)));}// 设置detailvoid 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、detailObject* key = KeyAt(i);if (key->IsNumber()) {uint32_t number = static_cast<uint32_t>(key->Number());// 命中则设置对应的索引的值,key、value、detail分别为null、null、0if (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;// 拿到keyObject* 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、detailset(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 - 1for (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);
}

这篇关于v8源码解析之Dictionary(v8 0.1.5)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/853412

相关文章

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI

Python 中的异步与同步深度解析(实践记录)

《Python中的异步与同步深度解析(实践记录)》在Python编程世界里,异步和同步的概念是理解程序执行流程和性能优化的关键,这篇文章将带你深入了解它们的差异,以及阻塞和非阻塞的特性,同时通过实际... 目录python中的异步与同步:深度解析与实践异步与同步的定义异步同步阻塞与非阻塞的概念阻塞非阻塞同步

Redis中高并发读写性能的深度解析与优化

《Redis中高并发读写性能的深度解析与优化》Redis作为一款高性能的内存数据库,广泛应用于缓存、消息队列、实时统计等场景,本文将深入探讨Redis的读写并发能力,感兴趣的小伙伴可以了解下... 目录引言一、Redis 并发能力概述1.1 Redis 的读写性能1.2 影响 Redis 并发能力的因素二、

Spring MVC使用视图解析的问题解读

《SpringMVC使用视图解析的问题解读》:本文主要介绍SpringMVC使用视图解析的问题解读,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring MVC使用视图解析1. 会使用视图解析的情况2. 不会使用视图解析的情况总结Spring MVC使用视图

利用Python和C++解析gltf文件的示例详解

《利用Python和C++解析gltf文件的示例详解》gltf,全称是GLTransmissionFormat,是一种开放的3D文件格式,Python和C++是两个非常强大的工具,下面我们就来看看如何... 目录什么是gltf文件选择语言的原因安装必要的库解析gltf文件的步骤1. 读取gltf文件2. 提

Java中的runnable 和 callable 区别解析

《Java中的runnable和callable区别解析》Runnable接口用于定义不需要返回结果的任务,而Callable接口可以返回结果并抛出异常,通常与Future结合使用,Runnab... 目录1. Runnable接口1.1 Runnable的定义1.2 Runnable的特点1.3 使用Ru

Spring 中 BeanFactoryPostProcessor 的作用和示例源码分析

《Spring中BeanFactoryPostProcessor的作用和示例源码分析》Spring的BeanFactoryPostProcessor是容器初始化的扩展接口,允许在Bean实例化前... 目录一、概览1. 核心定位2. 核心功能详解3. 关键特性二、Spring 内置的 BeanFactory

使用EasyExcel实现简单的Excel表格解析操作

《使用EasyExcel实现简单的Excel表格解析操作》:本文主要介绍如何使用EasyExcel完成简单的表格解析操作,同时实现了大量数据情况下数据的分次批量入库,并记录每条数据入库的状态,感兴... 目录前言固定模板及表数据格式的解析实现Excel模板内容对应的实体类实现AnalysisEventLis

Java的volatile和sychronized底层实现原理解析

《Java的volatile和sychronized底层实现原理解析》文章详细介绍了Java中的synchronized和volatile关键字的底层实现原理,包括字节码层面、JVM层面的实现细节,以... 目录1. 概览2. Synchronized2.1 字节码层面2.2 JVM层面2.2.1 ente