gpt4 book ai didi

javascript - V8 延迟生成堆栈跟踪似乎导致 vows 库中的无限循环

转载 作者:IT老高 更新时间:2023-10-28 23:16:55 27 4
gpt4 key购买 nike

我花了一些时间在 NodeJS 测试套件中调试一个奇怪的无限循环问题。它只在极少数情况下发生,但我可以在附加到 chrome 调试器时重现它。

我认为这与 V8 对 stack traces in exceptions 的处理有关。以及 vows library 的扩展名对 AssertionError 对象做了(誓言添加了一个 toString 方法)。也有可能是我弄错了,所以想问问我对V8实现的理解是否正确。

这是重现错误的最小示例:

$ git clone https://github.com/flatiron/vows.git
$ cd vows && npm install && npm install should

$ cat > example.js
var should = require('should');
var error = require('./lib/assert/error.js');

try {
'x'.should.be.json;
} catch (e) {
console.log(e.toString());
}

// without debug, it should fail as expected
$ node example.js
expected 'x' to have property 'headers' // should.js:61

// now with debug
$ node-inspector &
$ node --debug-brk example.js

// 1) open http://127.0.0.1:8080/debug?port=5858 in Chrome
// 2) set breakpoint at lib/assert/error.js#79 (the toString method)
// 3) Resume script execution (F8)

现在程序进入了一个无限循环:toString 方法(由 vows 库添加)在 this.stack 在第 83 行的正则表达式。

require('assert').AssertionError.prototype.toString = function () {
var that = this, // line 79: breakpoint
source;

if (this.stack) {
source = this.stack.match(/([a-zA-Z0-9._-]+\.(?:js|coffee))(:\d+):\d+/); // line 83: infinite loop takes place here (however, this.stack is undefined)
}

当我在调试器中检查 this 时,它显示它是一个 AssertionError 但它的 stack 属性是 undefined。但是,当我将鼠标悬停在它上面时,它会显示实际的堆栈跟踪。

我认为这种现象是由 V8 的惰性优化引起的。它仅按需计算堆栈跟踪。这样做会干扰 vows 添加的 toString 方法。 toString 方法再次访问堆栈跟踪 (this.stack),因此循环继续。

这个结论正确吗?如果是这样,有没有办法修补 vows 代码,使其适用于 V8(或者我至少可以将其报告为 vows 项目中的错误)?

我在 Ubuntu 下使用 node v0.10.28。

更新:没有誓言的简化示例

这是一个简化版本。它不再依赖于誓言,而是我只复制了它的 toString 扩展的相关部分:

var should = require('should');

require('assert').AssertionError.prototype.toString = function () {
var that = this,
source;

if (this.stack) {
source = this.stack.match(/([a-zA-Z0-9._-]+\.(?:js|coffee))(:\d+):\d+/);
}

return '<dummy-result>';
};

try {
'x'.should.be.json;
} catch (e) {
console.log(e.toString());
}

// expected result (without debug mode)
$ node example.js
<dummy-result>

在 Debug模式下,递归发生在 if 语句中。

更新:带有 ReferenceError 的更简单版本

ReferenceError.prototype.toString = function () {
var that = this,
source;

if (this.stack) {
source = this.stack.match(/([a-zA-Z0-9._-]+\.(?:js|coffee))(:\d+):\d+/);
}

return '<dummy-result>';
};

try {
throw new ReferenceError('ABC');
} catch (e) {
console.log(e.toString());
}

(我还创建了一个 jsfiddle 示例,但我无法在那里重现无限循环,只能使用 Node 。)

最佳答案

恭喜,您在 V8 中发现了一个错误!

是的,这绝对是该版本 Node 中 V8 版本中的一个错误。

V8 版本中的代码您的 Node 版本使用的代码类似于:

function FormatStackTrace(error, frames) {
var lines = [];
try {
lines.push(error.toString());
} catch (e) {
try {
lines.push("<error: " + e + ">");
} catch (ee) {
lines.push("<error>");
}
}

这是 NodeJS uses 版本的实际代码.

它正在做 error.toString() 的事实本身导致了循环,this.stack 访问 FormatStackTrace 反过来又在做 .toString()。你的观察是正确的。

如果这有什么好处的话,那段代码在新版本的 V8 中看起来很不一样。在 Node 0.11 中,this bug is already fixed .

这里是 commit that fixed it一年半前由 Vyacheslav Egorov 提交.

你能做些什么?

好吧,你的选择是有限的,但既然这是为了调试,总能阻止第二次迭代并停止循环:

ReferenceError.prototype.toString = function () {
var source;
if(this.alreadyCalled) return "ReferenceError";
this.alreadyCalled = true;
if (this.stack) {
source = this.stack.match(/([a-zA-Z0-9._-]+\.(?:js|coffee))(:\d+):\d+/);
}

return '<dummy-result>';
};

没有表现出同样的问题。如果不访问核心代码,您将无能为力。

关于javascript - V8 延迟生成堆栈跟踪似乎导致 vows 库中的无限循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23877022/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com