一个 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...

总结

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

相关推荐
Csvn6 分钟前
并发模式:让渲染学会排队、插队和让路
前端
可乐鸡翅yeah_19 分钟前
新手梳理:M3U8 线上问题,哪些是前端锅,哪些是后端锅
前端·ios·音视频·实时音视频·m3u8·音视频在线播放
Flynt20 分钟前
Linear 用 1000 个 PR 换掉 styled-components,我写了 200 个按钮,把这笔账复现了一遍
前端·css·preact
JavaGuide1 小时前
NVIDIA 又开源了!这次给 AI Agent 加上权限管控
前端·后端
excel2 小时前
prisma 如何处理数据库竞态
前端·数据库·后端
莪_幻尘3 小时前
Skill 体检:30 个 Skill 全凭感觉?体检器先自曝了 8 个“假 0 分
前端·人工智能·llm
风骏时光牛马3 小时前
AI模型综合能力评测:性能、指令遵循与多场景实测对比
前端
Frag0ut3 小时前
Chrome与Chromium内核浏览器在Windows 11上的新特性全景解析
前端·chrome·windows·web安全·chromium·gemini ai·playready drm
hiahiahia1234 小时前
实现完整 Tool Dispatcher
开发语言·前端
IT_陈寒4 小时前
Java线程池这破玩意,差点让我周末加班排查到凌晨
前端·人工智能·后端