13

该代码{}在 JavaScript 中是完全合法的,因为它代表一个Block

但是,我注意到{{...}}在 Chrome* 中嵌套很多块 ( ) 会引发另一个问题:

未捕获的 RangeError:超出最大调用堆栈大小

为什么这里会发生堆栈溢出?


这是一个说明问题的代码笔( jsfiddle崩溃)。

当在 JS Room 中询问时,Zirak发现幻数在 chrome 上是3913块,在 Firefox上是2555 。

什么被压入堆栈?为什么?


(*) 我已经检查过了,它也发生在 IE 和 Firefox 中

更新:我已经检查过,不可靠的 IE 能够避免堆栈溢出异常。它已经抛出了两次,但没有第三次。如果任何读者有 IE 并且愿意测试它的旧版本(如 IE8 和 9)并让我知道会发生什么,我将非常感激。

4

2 回答 2

18

首先,ghord 是完全正确的。这是由解析器的递归性质引起的,所以给他点赞吧。但是需要有证据,OP希望我将其作为单独的答案发布。

火狐

那么,在哪里可以找到它是如何完成的呢?问一些从事发动机制造的人。所以我走到#jsapi频道上irc://irc.mozilla.org问他们:

< bhackett> zirak: well, with a recursive descent parser all the productions will roughly correspond to a frame on the C stack

< bhackett> zirak: the parser is at js/src/frontent/Parser.cpp

< Waldo> zirak: Parser<ParseHandler>::statement(bool canHaveDirectives) and Parser<ParseHandler>::statements() pretty much

< bhackett> zirak: in this case, the recursion will be Parser::blockStatement ->Parser::statements -> Parser::statement -> Parser::blockStatement

这几乎是答案。转到 Mozilla 中央存储库并进行挖掘,我们有我们的嫌疑人:

所以,我们所拥有的是:

  • statements它调用blockStatement,它解析块,找到另一个块,调用
    • statements它调用blockStatement,它解析块,找到另一个块,调用
      • statements它调用blockStatement,它解析块,找到另一个块,调用
        • ...

直到堆栈崩溃,我猜这里

所以我们有 Firefox 的源代码。

基于 v8 的 Chrome/Chromium/其他任何东西

从 Firefox 中吸取教训,我去了 v8 项目并寻找一个名为parser. 果然,它就在那里!

接下来的事情是在解析块时寻找,所以我天真地搜索statements,到达有希望的ParseStatement

这是我们的幸运日,一个巨人switch第一个案例是我们关心的,一个电话,ParseBlock另一个有前途的名字!

确实,在内部ParseBlock,我们找到了对 的调用ParseStatement。所以,要清楚,我们有两个功能:

他们互相调用,就像我们在 Firefox 中看到的那样:

  • ParseStatement它调用ParseBlock,它解析块,找到另一个块,调用
    • ParseStatement它调用ParseBlock,它解析块,找到另一个块,调用
      • ParseStatement它调用ParseBlock,它解析块,找到另一个块,调用
        • ...

直到kaboom进入堆栈。

苹果浏览器

(很抱歉在上次编辑中称它为封闭源代码!) Safari 的 js 引擎是JavaScriptCore,它位于 WebKit 项目中。查找函数与为 Chrome 查找函数几乎相同,所以让我们跳到有趣的部分:

我们中间多了一个函数,但是原理是一样的:

  • parseSourceElementswhich calls parseStatementwhich callsparseBlockStatement解析块,找到另一个块,调用
    • parseSourceElementswhich calls parseStatementwhich callsparseBlockStatement解析块,找到另一个块,调用
      • parseSourceElementswhich calls parseStatementwhich callsparseBlockStatement解析块,找到另一个块,调用
        • ...

繁荣

IE(以及所有其他封闭源代码,如 Opera)

......将仍然是一个谜,除非他们突然有想要公开他们的资源的冲动,或者如果一位有进取心的员工与我们分享了内部信息。上面两个伟大的引擎以同样的方式做这件事,所以我们可以假设其他浏览器也做同样的事情。

如果浏览器没有崩溃,那是一个有趣的问题,但这个答案不能指望咳嗽答案。

于 2013-06-25T20:40:13.440 回答
14

递归下降解析器的默认实现简单而优雅,用一种方法解析每个语言语法规则。这些方法递归调用其他方法,所以当嵌套规则太多时,就会超出堆栈大小。Chrome 和 Firefox 都使用这种解释器实现。

你会注意到很多'+',虽然与范围无关,但会导致同样的异常:

+ + + + + + + + + ... // same error
于 2013-06-25T20:16:24.083 回答