代码{}
在JavaScript中完全合法,因为它代表Block。
然而,我注意到在Chrome *中将很多块({{...}}
)嵌套在另一个加注中:
未捕获RangeError:超出最大调用堆栈大小
为什么是在这里发生堆栈溢出?
这是一个codepen来说明问题(jsfiddle崩溃)。
在JSRoom Zirak中询问时,发现Chrome上的幻数为 3913 块,Firefox上为 2555 。
什么被推到了堆栈?为什么呢?
(*)我已经检查过,它也发生在IE和Firefox中
更新:我已经检查过并且不可靠IE可以避免堆栈溢出异常。它抛出了两次而不是第三次。如果任何读者都有IE并且愿意测试它的旧版本(比如IE8和9)并且让我知道会发生什么,我真的很感激。
答案 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的来源。
从Firefox学习我的课程,我去了v8项目,找了一个名为parser
的文件。果然,它就在那里!
接下来的事情是寻找解析块的时间,所以我天真地搜索statements
,到达有希望的ParseStatement。
这是我们的幸运日,一个巨人switch
!而the first case就是我们所关心的,呼唤ParseBlock
,另一个有前途的名字!
确实,inside ParseBlock
, we find a call到ParseStatement
。所以,很明显,我们有两个功能:
他们像我们在Firefox中看到的那样互相打电话:
ParseStatement
调用解析块的ParseBlock
,找到另一个块,调用
ParseStatement
调用解析块的ParseBlock
,找到另一个块,调用
ParseStatement
调用解析块的ParseBlock
,找到另一个块,调用
直到kaboom进入筹码。
(很抱歉在最后一次编辑中将其称为闭源!)Safari的js引擎是JavaScriptCore,它位于WebKit项目中。查找功能几乎与为Chrome查找功能完全相同,所以让我们跳到有趣的部分:
Parser<LexerType>::parseSourceElements
Parser<LexerType>::parseStatement
Parser<LexerType>::parseBlockStatement
我们在中间有一个额外的功能,但原理是相同的:
parseSourceElements
调用parseStatement
调用parseBlockStatement
来解析块,找到另一个块,调用
parseSourceElements
调用parseStatement
调用parseBlockStatement
来解析块,找到另一个块,调用
parseSourceElements
调用parseStatement
调用parseBlockStatement
来解析块,找到另一个块,调用
BOOM
如果浏览器没有崩溃,这是一个有趣的问题,但是这个答案不能指望咳嗽回答。
答案 1 :(得分:14)
Recursive descend parser的默认实现,虽然简单而优雅,但使用一种方法解析每种语言语法规则。这些方法以递归方式调用其他方法,因此当您有太多嵌套规则时,它会超出堆栈大小。 Chrome和Firefox都使用这种解释器实现。
您会注意到很多“+”虽然与范围无关,但会导致相同的异常:
+ + + + + + + + + ... // same error