热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

《ChromeV8源码》44.Runtime_StringToArray源码、触发条件

 

 

1 介绍

Runtime 是一系列采用 C++ 语言编写的功能方法,它实现了大量 Javascript 运行期间需要的 native 功能。接下来几篇文章将介绍一些 Runtime 方法。本文分析 Runtime_StringToArray 方法的源码和重要数据结构,讲解 Runtime_StringToArray 方法的触发条件。

注意: Runtime 方法的加载、调用以及 RUNTIME_FUNCTION 宏模板请参见第十六篇文章。—allow-natives-syntax 和 %-prefix 不是本文的讲解重点。

 

2 StringToArray 测试用例

字符串转数组(StringToArray)主要应用在 String.prototype.split 源码中,StringToArray 功能的实现有两个,一个是 StringBuiltinsAssembler::StringToArray()(参见第 39 篇文章),另一个是 Runtime_StringToArray()。字符串转数组时,先使用 StringBuiltinsAssembler::StringToArray() 功能,当该功能 不适合 时转而采用效率较低的 Runtime_StringToArray() 功能。下面从 split 源码说起:

1. TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
2. // If the separator string is empty then return the elements in the subject.
3. {//.............省略.....................
4. Label next(this);
5. GotoIfNot(SmiEqual(LoadStringLengthAsSmi(separator_string), smi_zero),
6. &next);
7. TNode subject_length = LoadStringLengthAsSmi(subject_string);
8. GotoIf(SmiEqual(subject_length, smi_zero), &return_empty_array);
9. args.PopAndReturn(
10. StringToArray(context, subject_string, subject_length, limit_number));
11. BIND(&next);
12. }//.............省略.....................
13. }

上述代码中,第 5 行代码检测 separator_string 的长度不为零时,跳转到 11 行;
第 7-8 行代码判断字符串是否为空,如果为空则返回空数组;
第 9 行代码生成并返回由字符组成的数组,也就是 string.split(“”) 的结果。
下面给出 StringBuiltinsAssembler::StringToArray() 源码:

1. TNode StringBuiltinsAssembler::StringToArray(
2. TNode context, TNode subject_string,
3. TNode subject_length, TNode limit_number) {
4. //省略..................
5. to_direct.TryToDirect(&call_runtime);
6. BIND(&call_runtime);
7. {
8. result_array = CAST(CallRuntime(Runtime::kStringToArray, context,
9. subject_string, limit_number));
10. Goto(&done);
11. }
12. BIND(&done);
13. return result_array.value();
14. }

上述代码的详细功能参见第 39 篇文章,第 5 行代码转换失败时则跳转到第 8 行代码(Runtime_StringToArray),也就符合了前面提到的 “不适合” 条件。
TryToDirect 功能参见之前的文章,下面给出最终的测试用例:

var str1="chromium";
var str2="blink";
var str3 = "Understanding"
var cOns= str1+str2+str3.substring(3);
console.log(cons.split(""));

上述代码中, cons 变量中包括了 “str1 + str2” 的结果,该结果的类型是 ConsString,也包括了 str3 的子部分,所以会导致 TryToDirect 失败。

 

3 Runtime_StringToArray 源码

源码如下:

1. RUNTIME_FUNCTION(Runtime_StringToArray) {
2. HandleScope scope(isolate);
3. DCHECK_EQ(2, args.length());
4. CONVERT_ARG_HANDLE_CHECKED(String, s, 0);
5. CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
6. s = String::Flatten(isolate, s);
7. const int length =
8. static_cast(std::min(static_cast(s->length()), limit));
9. Handle elements;
10. int position = 0;
11. if (s->IsFlat() && s->IsOneByteRepresentation()) {
12. elements = isolate->factory()->NewFixedArray(length);
13. DisallowGarbageCollection no_gc;
14. String::FlatContent cOntent= s->GetFlatContent(no_gc);
15. if (content.IsOneByte()) {
16. base::Vector chars = content.ToOneByteVector();
17. position = CopyCachedOneByteCharsToArray(isolate->heap(), chars.begin(),
18. *elements, length);
19. } else {
20. MemsetTagged(elements->data_start(),
21. ReadOnlyRoots(isolate).undefined_value(), length);
22. }
23. } else {
24. elements = isolate->factory()->NewFixedArray(length);
25. }
26. for (int i = position; i 27. Handle str =
28. isolate->factory()->LookupSingleCharacterStringFromCode(s->Get(i));
29. elements->set(i, *str);
30. }
31. #ifdef DEBUG
32. for (int i = 0; i 33. DCHECK_EQ(String::cast(elements->get(i)).length(), 1);
34. }
35. #endif
36. return *isolate->factory()->NewJSArrayWithElements(elements);
37. }

上述代码中,第 6 行代码执行字符串 Flatten;
第 7 行获得字符串长度 length,并做最大长度限制检测 limit;
第 11 行检测字符串是否为 OneByte 类型;
第 12 行申请长度为 length 的 Array 数组 elements;
第 14 行获得 FlatContent 内存;
第 16~17 行把 FlatContent 内容以字符为粒度逐个拷贝到 elements 数组中;
第 20~24 行创建 Array 数组 elements;
第 26~29 行从 Cache 中查找相应的字符并填充进 elements;
第 36 行使用 elements 创建 JSArray 并返回结果。
下面说明 Runtime_StringToArray 中使用的重要函数:
(1) LookupSingleCharacterStringFromCode,V8 内部字符串缓存功能

1. Handle Factory::LookupSingleCharacterStringFromCode(uint16_t code) {
2. if (code <= unibrow::Latin1::kMaxChar) {
3. {
4. Object value = single_character_string_cache()->get(code);
5. }
6. Handle result =
7. InternalizeString(base::Vector(buffer, 1));
8. single_character_string_cache()->set(code, *result);
9. return result;
10. }
11. uint16_t buffer[] = {code};
12. return InternalizeString(base::Vector(buffer, 1));
13. }

V8 使用 string cache 保存常用的、可以复用的字符,这些字符的类型是内部字符。上述代码中,第 2-4 行代码使用 int16 类数值 code 在 ASCII 范围内查找 cache;
第 6 行申请新的内部字符串,并保存到 string cache 中;
第 11~12 行申请新的内部字符串,这些字符串超了 ASCII 范围,重复使用的概率很小,所以不缓存 string cache。
(2) InternalizeString,内部字符串生成函数

1. template
2. Handle FactoryBase::InternalizeString(
3. const base::Vector& string, bool convert_encoding) {
4. SequentialStringKey key(string, HashSeed(read_only_roots()),
5. convert_encoding);
6. return InternalizeStringWithKey(&key);
7. }
8. template
9. Handle FactoryBase::InternalizeString(
10. const base::Vector& string, bool convert_encoding) {
11. SequentialStringKey key(string, HashSeed(read_only_roots()),
12. convert_encoding);
13. return InternalizeStringWithKey(&key);
14. }

上述代码从 int8 和 int16 两个方面分别实现 InternalizeString(),核心功能由 InternalizeStringWithKey() 实现。
Runtime_StringToArray 方法的最后一行代码将 elements 包装成 NewJSArrayWithElements 数组并返回结果。
(3) NewJSArrayWithElements,JSArray生成函数
源码如下:

1. Handle Factory::NewJSArrayWithUnverifiedElements(
2. Handle elements, ElementsKind elements_kind, int length,
3. AllocationType allocation) {
4. DCHECK(length <= elements->length());
5. NativeContext native_cOntext= isolate()->raw_native_context();
6. Map map = native_context.GetInitialJSArrayMap(elements_kind);
7. if (map.is_null()) {
8. JSFunction array_function = native_context.array_function();
9. map = array_function.initial_map();
10. }
11. Handle array = Handle::cast(
12. NewJSObjectFromMap(handle(map, isolate()), allocation));
13. DisallowGarbageCollection no_gc;
14. JSArray raw = *array;
15. raw.set_elements(*elements);
16. raw.set_length(Smi::FromInt(length));
17. return array;
18. }

上述代码中,第 2 行 elements 是之前申请的字符串数组;elements_kind 采用默认的 TERMINAL_FAST_ELEMENTS_KIND;
第 6 行根据 elements_kind 获取初始 map;
第 8~9 行 map 为空时采用 array_function 初始化 map;
第 11 行创建 JSArray 数组对象 array;
第 15~16 行把 elements 和 length 保存到 array 中。
(4) NewJSObjectFromMap,申请JSArray内存空间

1. Handle Factory::NewJSObjectFromMap(
2. Handle map, AllocationType allocation,
3. Handle allocation_site) {
4. // JSFunctions should be allocated using AllocateFunction to be
5. // properly initialized.
6. DCHECK(!InstanceTypeChecker::IsJSFunction((map->instance_type())));
7. // Both types of global objects should be allocated using
8. // AllocateGlobalObject to be properly initialized.
9. DCHECK(map->instance_type() != JS_GLOBAL_OBJECT_TYPE);
10. JSObject js_obj = JSObject::cast(
11. AllocateRawWithAllocationSite(map, allocation, allocation_site));
12. InitializeJSObjectFromMap(js_obj, *empty_fixed_array(), *map);
13. DCHECK(js_obj.HasFastElements() || js_obj.HasTypedArrayElements() ||
14. js_obj.HasFastStringWrapperElements() ||
15. js_obj.HasFastArgumentsElements() || js_obj.HasDictionaryElements());
16. return handle(js_obj, isolate());
17. }

上述代码中,第 6、9 行代码检测 type 类型;第 10 行代码根据 map 获取内存空间 js_obj;第 12 行代码 InitializeJSObjectFromMap 使用 empty_fixed_array 初始化 js_obj。
返回 js_obj 到 NewJSArrayWithUnverifiedElements 方法中,然后设置 elements 和 length,最终完成 JSArray 的申请。
图 1 给出 Runtime_StringToArray 的调用堆栈,供读者复现。

技术总结
(1) StringBuiltinsAssembler::StringToArray 方法效率最高,Runtime_StringToArray 是它的备选方案;
(2) JSArray 对象使用 FixArray 存储数据;
(3) INTERNALIZED_STRING_TYPE 是 V8 的字符串类型,此外还有 ConsString、Sliced 等,具体参见枚举类 InstanceType。

好了,今天到这里,下次见。
个人能力有限,有不足与纰漏,欢迎批评指正
微信:qq9123013 备注:v8交流 邮箱:v8blink@outlook.com


推荐阅读
  • 本文介绍了一个React Native新手在尝试将数据发布到服务器时遇到的问题,以及他的React Native代码和服务器端代码。他使用fetch方法将数据发送到服务器,但无法在服务器端读取/获取发布的数据。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 先看看ElementUI里关于el-table的template数据结构:<template><el-table:datatableData><e ... [详细]
  • 使用eclipse创建一个Java项目的步骤
    本文介绍了使用eclipse创建一个Java项目的步骤,包括启动eclipse、选择New Project命令、在对话框中输入项目名称等。同时还介绍了Java Settings对话框中的一些选项,以及如何修改Java程序的输出目录。 ... [详细]
  • 本文介绍了如何使用n3-charts绘制以日期为x轴的数据,并提供了相应的代码示例。通过设置x轴的类型为日期,可以实现对日期数据的正确显示和处理。同时,还介绍了如何设置y轴的类型和其他相关参数。通过本文的学习,读者可以掌握使用n3-charts绘制日期数据的方法。 ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • 本文是一篇翻译文章,介绍了async/await的用法和特点。async关键字被放置在函数前面,意味着该函数总是返回一个promise。文章还提到了可以显式返回一个promise的方法。该特性使得async/await更易于理解和使用。本文还提到了一些可能的错误,并希望读者能够指正。 ... [详细]
  • [echarts] 同指标对比柱状图相关的知识介绍及应用示例
    本文由编程笔记小编为大家整理,主要介绍了echarts同指标对比柱状图相关的知识,包括对比课程通过率最高的8个课程和最低的8个课程以及全校的平均通过率。文章提供了一个应用示例,展示了如何使用echarts制作同指标对比柱状图,并对代码进行了详细解释和说明。该示例可以帮助读者更好地理解和应用echarts。 ... [详细]
  • 本文介绍了利用ARMA模型对平稳非白噪声序列进行建模的步骤及代码实现。首先对观察值序列进行样本自相关系数和样本偏自相关系数的计算,然后根据这些系数的性质选择适当的ARMA模型进行拟合,并估计模型中的位置参数。接着进行模型的有效性检验,如果不通过则重新选择模型再拟合,如果通过则进行模型优化。最后利用拟合模型预测序列的未来走势。文章还介绍了绘制时序图、平稳性检验、白噪声检验、确定ARMA阶数和预测未来走势的代码实现。 ... [详细]
  • mysqldinitializeconsole失败_mysql03误删除了所有用户解决办法
    误删除了所有用户解决办法第一种方法(企业常用)1.将数据库down掉[rootdb03mysql]#etcinit.dmysqldstopShuttingdownMySQL..SU ... [详细]
author-avatar
心情爱心_634
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有