生成器的{Javascript堆栈模型

时间:2018-02-19 19:43:38

标签: javascript stack stack-trace v8 spidermonkey

当我使用javascript生成器为小型方案解释器实现调试器时,我开始想知道例如堆栈模型。 chrome javascript引擎。通常,只有一个堆栈用于函数调用帧。在生成器的情况下,我可以让函数调用执行另一个路径,然后跳回到部分执行的生成器,即将堆栈的一部分放入剩下的生命中。

如何实施,例如在chrome或firefox javascript引擎?整个虚拟堆栈是由多个虚拟堆栈组成还是堆叠在生成写入生成器对象时留下的部分?然后它可以在再次进入发电机时重新放回堆栈。

2 个答案:

答案 0 :(得分:4)

生成器仍然运行在与普通函数相同的单个调用堆栈上。评估之间没有多个堆栈跳过。

当你实例化一个生成器(通过调用生成器函数)然后调用它的.next()方法时,它只是将该调用推送到堆栈的顶部。然后它将在生成器函数内运行代码 当它遇到yield语句时,它只是从堆栈中弹出调用并从.next()方法返回,在任何函数调用之后继续照常进行。

生成器调用和正常函数调用之间的区别在于进入和离开代码时发生的情况 正常函数离开函数体的末尾或return / throw语句,然后完成。生成器也离开yield,但它必须记住状态(基本上将指令指针存储在生成器实例中),以便它可以在yield之后继续执行。此外,它必须记住所有局部变量的状态,但引擎已经知道如何通过闭包的实现来做到这一点 普通函数通过设置新环境并在函数体顶部开始执行来进入调用。生成器调用将恢复状态,以便它可以从中断处继续。

堆栈的正常行为不受此影响。

好的,我撒了谎。 yield*使一切变得复杂。递归yield* ed生成器链在进入或离开.next()调用时需要推送和弹出多个堆栈帧。引擎可以通过使用多个堆栈来优化此上下文切换。仍然,人们会看到它们堆叠在一起,形成一个大堆栈,并且在执行期间只会操纵该单个堆栈的顶部。

答案 1 :(得分:3)

在Chrome / V8的当前实现中,作为yield的一部分,生成器稍后恢复执行所需的所有状态都将写入对象。函数调用帧只有一个堆栈。

细节很复杂;如果您想阅读来源,请从(v8)/src/interpreter/bytecode-generator.cc中的BytecodeGenerator::VisitYield开始。