一个 TODO 引发的血案

Chrome 都用过吧,V8 都知道吧,V8 性能杠杠的没问题吧。但是你知道吗,有这样一段神奇的代码因为 V8 的优化导致了与实际设计预期的行为发生了相悖。

Object.defineProperty

  • Object.defineProperty([], 'length', {value: -1, configurable: true})
  • Object.defineProperty([], 'len' + 'gth', {value: -1, configurable: true})

我们可以来看一下这俩段代码,从 JavaScript 的语义上来说,这俩段代码并没有什么不同,但是当我们将他们运行在 Chrome 或者 Node.js 中,我们便能得到俩个截然不同的错误。

javascript 复制代码
Object.defineProperty([], 'length', {value: -1, configurable: true})
VM8179:1 Uncaught RangeError: Invalid array length
    at Function.defineProperty (<anonymous>)
    at <anonymous>:1:8
(anonymous) @ VM8179:1

Object.defineProperty([], 'len' + 'gth', {value: -1, configurable: true})
VM8183:2 Uncaught TypeError: Cannot redefine property: length
    at Function.defineProperty (<anonymous>)
    at <anonymous>:2:8

为什么呢?

那这和 V8 性能优化极好又有什么关系呢?我们可以在他们的源码中看到如下内容:

github.com/v8/v8/blob/...

cpp 复制代码
  if (*name == ReadOnlyRoots(isolate).length_string()) {
    // 2a. Return ArraySetLength(A, Desc).
    return ArraySetLength(isolate, o, desc, should_throw);
  }

简单理解一下,就是在这里直接使用了引用与系统内的常量字符串 "length" 引用进行了一个 O(1) 的快速匹配,从而优化了性能,但是也导致了我们上面的错误。

那么为什么 'length''len' + 'gth' 有区别呢?这个没有优化吗?我们可以在这《Exploring V8's strings: implementation and optimizations》看到实际上在 V8 中存在着不同的字符串,当我们使用 %DebugPrint 对俩段内容进行输出的时候,我们可以发现:

  • %DebugPrint('leng'+'th'):ONE_BYTE_STRING_TYPE
  • %DebugPrint('length'):ONE_BYTE_INTERNALIZED_STRING_TYPE

这也是为什么后者没办法作为一个常量字符串和全局只读常量字符串 "length" 进行比较的原因。

标题党!

你可能会问,你说的我都看懂了,但是和 TODO 又有什么关系呢?经常写代码都知道,我们不想处理的问题,都是先 TODO NEVER DO ,其实我们刚刚的代码上面看到一段东西:

github.com/v8/v8/blob/...

arduino 复制代码
  // TODO(jkummerow): Check if we need slow string comparison.

是不是很有趣呢 V8,哈哈哈哈,希望看完这些你能对 JS 一些底层的内容产生兴趣。

后续进展

在去年年底的时候,我与 TC39 的成员 Jack Works 讨论了这个问题,最后得到了一个结论,在 ECMAScript 的设计上来看,这确实是 V8 的一个问题(虽然它是为了优化性能),但是 TC39 也没有料到 V8 的优化这么细节,居然导致了上面的问题。所以在那段时间,他们对于该行为进行了约束,并且在 TC39 的标准中加入了该检测。

github.com/tc39/test26...

当时我在 v8 bugs 中也反映了该问题,他们在最近也对这个问题进行了修复。

bugs.chromium.org/p/v8/issues...

总结

挺开心的,有一种建设世界的快乐了。

相关推荐
巴拉巴拉~~7 小时前
KMP 算法通用图表组件:KmpChartWidget 多维度可视化 + PMT 表渲染 + 性能对比
前端·javascript·microsoft
智算菩萨7 小时前
基于spaCy的英文自然语言处理系统:低频词提取与高级文本分析
前端·javascript·easyui
刘一说7 小时前
Vue单页应用(SPA)开发全解析:从原理到最佳实践
前端·javascript·vue.js
疯狂成瘾者7 小时前
前端vue核心知识点
前端·javascript·vue.js
Laravel技术社区8 小时前
用PHP8实现斗地主游戏,实现三带一,三带二,四带二,顺子,王炸功能(第二集)
前端·游戏·php
m0_738120729 小时前
应急响应——知攻善防Web-3靶机详细教程
服务器·前端·网络·安全·web安全·php
hh随便起个名15 小时前
力扣二叉树的三种遍历
javascript·数据结构·算法·leetcode
我是小路路呀16 小时前
element级联选择器:已选中一个二级节点,随后又点击了一个一级节点(仅浏览,未确认选择),此时下拉框失去焦点并关闭
javascript·vue.js·elementui
程序员爱钓鱼16 小时前
Node.js 编程实战:文件读写操作
前端·后端·node.js
PineappleCoder16 小时前
工程化必备!SVG 雪碧图的最佳实践:ID 引用 + 缓存友好,无需手动算坐标
前端·性能优化