为什么嵌套一堆块会导致JavaScript中的堆栈溢出

时间:2013-06-25 19:56:53

标签: javascript stack-overflow

代码{}在JavaScript中完全合法,因为它代表Block

然而,我注意到在Chrome *中将很多块({{...}})嵌套在另一个加注中:

  

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

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


这是一个codepen来说明问题(jsfiddle崩溃)。

在JSRoom Zirak中询问时,发现Chrome上的幻数为 3913 块,Firefox上为 2555

什么被推到了堆栈?为什么呢?


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

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

2 个答案:

答案 0 :(得分: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,找到另一个块,调用
        • ...

在堆栈崩溃之前,我猜是here

所以我们有Firefox的来源。

Chrome / Chromium /基于v8

的任何其他内容

从Firefox学习我的课程,我去了v8项目,找了一个名为parser的文件。果然,它就在那里!

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

这是我们的幸运日,一个巨人switch!而the first case就是我们所关心的,呼唤ParseBlock,另一个有前途的名字!

确实,inside ParseBlock, we find a callParseStatement。所以,很明显,我们有两个功能:

他们像我们在Firefox中看到的那样互相打电话:

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

直到kaboom进入筹码。

Safari浏览器

(很抱歉在最后一次编辑中将其称为闭源!)Safari的js引擎是JavaScriptCore,它位于WebKit项目中。查找功能几乎与为Chrome查找功能完全相同,所以让我们跳到有趣的部分:

我们在中间有一个额外的功能,但原理是相同的:

  • parseSourceElements调用parseStatement调用parseBlockStatement来解析块,找到另一个块,调用
    • parseSourceElements调用parseStatement调用parseBlockStatement来解析块,找到另一个块,调用
      • parseSourceElements调用parseStatement调用parseBlockStatement来解析块,找到另一个块,调用
        • ...

BOOM

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

......仍然是一个谜,除非他们突然想要打开他们的消息来源,或者一个有进取心的员工与我们分享内幕。上面两个伟大的引擎以相同的方式进行,因此我们可以假设其他浏览器也是这样做的。

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

答案 1 :(得分:14)

Recursive descend parser的默认实现,虽然简单而优雅,但使用一种方法解析每种语言语法规则。这些方法以递归方式调用其他方法,因此当您有太多嵌套规则时,它会超出堆栈大小。 Chrome和Firefox都使用这种解释器实现。

您会注意到很多“+”虽然与范围无关,但会导致相同的异常:

+ + + + + + + + + ... // same error
相关问题