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实现批量访问URL并解析XML响应功能

《使用Python实现批量访问URL并解析XML响应功能》在现代Web开发和数据抓取中,批量访问URL并解析响应内容是一个常见的需求,本文将详细介绍如何使用Python实现批量访问URL并解析XML响... 目录引言1. 背景与需求2. 工具方法实现2.1 单URL访问与解析代码实现代码说明2.2 示例调用

SSID究竟是什么? WiFi网络名称及工作方式解析

《SSID究竟是什么?WiFi网络名称及工作方式解析》SID可以看作是无线网络的名称,类似于有线网络中的网络名称或者路由器的名称,在无线网络中,设备通过SSID来识别和连接到特定的无线网络... 当提到 Wi-Fi 网络时,就避不开「SSID」这个术语。简单来说,SSID 就是 Wi-Fi 网络的名称。比如

SpringCloud配置动态更新原理解析

《SpringCloud配置动态更新原理解析》在微服务架构的浩瀚星海中,服务配置的动态更新如同魔法一般,能够让应用在不重启的情况下,实时响应配置的变更,SpringCloud作为微服务架构中的佼佼者,... 目录一、SpringBoot、Cloud配置的读取二、SpringCloud配置动态刷新三、更新@R

使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)

《使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)》在现代软件开发中,处理JSON数据是一项非常常见的任务,无论是从API接口获取数据,还是将数据存储为JSON格式,解析... 目录1. 背景介绍1.1 jsON简介1.2 实际案例2. 准备工作2.1 环境搭建2.1.1 添加

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

在C#中合并和解析相对路径方式

《在C#中合并和解析相对路径方式》Path类提供了几个用于操作文件路径的静态方法,其中包括Combine方法和GetFullPath方法,Combine方法将两个路径合并在一起,但不会解析包含相对元素... 目录C#合并和解析相对路径System.IO.Path类幸运的是总结C#合并和解析相对路径对于 C

Java解析JSON的六种方案

《Java解析JSON的六种方案》这篇文章介绍了6种JSON解析方案,包括Jackson、Gson、FastJSON、JsonPath、、手动解析,分别阐述了它们的功能特点、代码示例、高级功能、优缺点... 目录前言1. 使用 Jackson:业界标配功能特点代码示例高级功能优缺点2. 使用 Gson:轻量

Java如何接收并解析HL7协议数据

《Java如何接收并解析HL7协议数据》文章主要介绍了HL7协议及其在医疗行业中的应用,详细描述了如何配置环境、接收和解析数据,以及与前端进行交互的实现方法,文章还分享了使用7Edit工具进行调试的经... 目录一、前言二、正文1、环境配置2、数据接收:HL7Monitor3、数据解析:HL7Busines

python解析HTML并提取span标签中的文本

《python解析HTML并提取span标签中的文本》在网页开发和数据抓取过程中,我们经常需要从HTML页面中提取信息,尤其是span元素中的文本,span标签是一个行内元素,通常用于包装一小段文本或... 目录一、安装相关依赖二、html 页面结构三、使用 BeautifulSoup javascript

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库