在iOS 10 beta 2和beta 3(在iPhone 6上运行)上运行时,我们的JavaScript应用程序以非常奇怪的方式失败。查看日志时,我可以看到数组在意外的地方包含NaN和0x00。我设法制作了一个可以非常可靠地重现行为的测试程序。
我已经向Apple填写了一个错误报告,但没有收到回复,所以我有点担心这是否会被修复。所以作为第一件事,我想听听是否有人可以重现它并且至少确认存在错误(而不仅仅是我的误解等等)。我自己很确定这一点,但总是很安全!也可能是其他人遇到过它并找到了解决方法,或者如果某些WebKit开发人员遇到它,他们可能会提供帮助。
测试程序显示错误 这是测试程序。问题不会每次都发生,但页面中的JavaScript代码可以检测到它何时发生,并将一直刷新页面直到它发生。在不受影响的浏览器上,这意味着页面将保持刷新。在受影响的浏览器上(例如iOS 10 beta 2上的Safari和iPhone 6上运行的beta 3),刷新将在一些迭代(通常为5-10)和显示错误后停止。
该程序通过创建一个大小为8192的uint8array来运行(似乎较小的数组大小会导致错误更加罕见)。它将用虚拟值填充此数组,然后调用“toStr”,它首先分配一个新的plain数组,然后将uint8array的内容复制到plain数组,沿途缩小每个元素。执行此操作时,它会构建一个包含原始值和复制值的字符串。当错误发生时,目标数组中的元素变成NaN,由于它通过构造仅包含整数,因此不应该发生。 doTest()函数测试结果字符串中是否包含这样的NaN值,表明发生了错误。
请注意,在每次刷新过程中,程序运行20次迭代,此时它也是随机的,哪次迭代失败。但是,我观察到如果在前20次迭代中没有发生错误,那么即使一次运行无限次迭代,也不可能在此页面加载中发生错误,这就是我添加的原因重新加载页面逻辑。
请注意,程序逻辑本身是完全确定的,因此每次运行应该是相同的,其他浏览器也是如此。通过从doTest()函数中的log()语句中删除注释,可以看到更多细节,以便打印出数组。
请注意,如果函数“narrow”被内联而不是单独的函数,问题似乎消失了(即使这两个程序当然应该在语义上等效)。此外,如果忽略从“state>> 8”的转换,错误似乎也消失了,因此确切的数组值是重要的,或者这种重写会以某种方式影响JIT的运行方式。因此,在测试问题时使用确切的程序结构至关重要。
<html>
<head>
<title>Array test page</title>
<script>
log = function(s) {
var ta = document.getElementById("ta");
ta.value += s + "\n";
}
function narrow(x_0) {
return x_0 << 24 >> 24;
}
function toStr(i8bytes) {
var bytes, i, str;
bytes = new Array(i8bytes.length);
str = '';
for (i = 0; i < 16; i++) {
bytes[i] = narrow(i8bytes[i]);
str += '*** (' + i8bytes[i] + ' - ' + bytes[i] + ')';
}
return str;
}
function doTest() {
var sz = 8192;
state = 0;
var i;
var fnd = false;
for (i = 0; i < 20; i++) {
var arr = new ArrayBuffer(sz);
var i8bytes = new Uint8Array(arr, 0, sz);
for (j = 0; j < i8bytes.length; j++) {
state = state + 1;
var v = state >> 8;
i8bytes[j] = narrow(v);
}
var str = toStr(i8bytes);
// log(str); <-- REMOVE COMMENT to show results
if (str.indexOf("NaN") !== -1) {
log("Found NaN at iteration" + i);
log(str);
fnd = true;
break;
}
}
return fnd;
}
function start() {
log("Starting: " + new Date());
if (!doTest()) {
location.reload(true); // <--- REMOVE THIS LINE TO PREVENT RELOAD
}
};
</script>
</head>
<body onload="start()">
<h1>Array test page</h1>
<p>Note that on a non-affected browser this page will reload indefinitely. On an affected browser, it
will stop reloading once the problem is detected.
<p>
<textarea id="ta" rows="10" cols="40"></textarea>
</body>
</html>