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

Ben*_*aum 13 javascript stack-overflow

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

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

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

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


这是一个说明问题的本(jsfiddle崩溃).

在JSRoom询问时,Zirak发现魔术数字在Chrome 上为3913块,在Firefox 上为2555块.

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


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

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

Zir*_*rak 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的来源.

Chrome/Chromium /基于v8的任何其他内容

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

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

这是我们的幸运日,一个巨人switch!和第一种情况是我们所关心的,一个电话ParseBlock,另一名有前途!

事实上,ParseBlock,我们发现一个电话ParseStatement.所以,要清楚,我们有两个功能:

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

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

直到kaboom去堆栈.

苹果浏览器

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

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

  • parseSourceElements调用parseStatement哪个调用parseBlockStatement解析块,调用另一个块
    • parseSourceElements调用parseStatement哪个调用parseBlockStatement解析块,调用另一个块
      • parseSourceElements调用parseStatement哪个调用parseBlockStatement解析块,调用另一个块
        • ...

繁荣

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

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

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


gho*_*ord 14

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

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

+ + + + + + + + + ... // same error
Run Code Online (Sandbox Code Playgroud)

  • +1有趣的是,编译时的StackOverflow在执行期间与报告的错误消息无法区分StackOverflow. (3认同)