Seb*_*ian 19 javascript safari jit safari6
我们发现我们的Javascript代码的解释存在严重问题,这些代码仅发生在iOS 5/Safari 6(当时的iPad版本)上,我们认为这是由于Safari中的Just in Time JS编译器中的关键错误.(请参阅下面的更新,了解更多受影响的版本和现在似乎包含修复的版本).
我们最初在我们的库的在线演示中发现了这个问题:演示或多或少地随机崩溃,但这只是第二次(甚至更晚)发生相同代码的执行.即如果你运行一部分代码,一切正常,但后续运行会使应用程序崩溃.
有趣的是,在Chrome for iOS中执行相同的代码问题没有显示,我们认为这是由于Chrome for iOS中使用的Webview缺少JIT功能.
经过大量的摆弄,我们终于认为我们发现了至少一个有问题的代码:
var a = 0; // counter for index
for (var b = this.getStart(); b !== null; b = b.getNext()) // iterate over all cells
b.$f = a++; // assign index to cell and then increment
Run Code Online (Sandbox Code Playgroud)
本质上,这是一个简单的for循环,它将链表中的每个单元格分配给它的索引.这里的问题是循环体中的后增量操作.当前计数被分配给字段并在计算表达式后更新,基本上与首先分配a然后将其递增1相同.
这在我们测试的所有浏览器和Safari中的前几次都可以正常工作,然后突然看起来似乎计数器变量a首先递增,然后分配结果,就像预增量操作一样.
我创建了一个小提琴,显示了这里的问题:http://jsfiddle.net/yGuy/L6t5G/
在带有iOS 6的iPad 2上运行示例并且所有更新结果对于我的情况下的前两次运行是正常的,并且在第三次运行中突然显示列表中的最后一个元素具有指定的值,其中一个是关闭的(输出当你点击"点击我"按钮从"从0到500"变为"从0到501"时
有趣的是,如果你切换标签,或稍等一下,可能会发生突然结果是正确的两次左右的运行!似乎Safari有时重置是JIT缓存.
所以,因为我认为Safari团队可能需要很长时间来修复这个bug(我还没有报告),并且可能还有其他类似的错误,比如潜伏在JIT中同样难以找到的,我想知道是否有办法在Safari中禁用JIT功能.当然这会降低我们的代码(已经非常占用CPU),但速度比崩溃慢.
更新:不出所料,它不仅仅是受影响的后增量运算符,还有后减量运算符.更令人惊讶和更令人担忧的是,如果赋值,则没有区别,因此在现有代码中查找赋值是不够的.例如,下面的代码b.$f = (a++ % 2 == 0) ? 1 : 2;中,变量值未分配,但仅用于三元运算符条件,在某种意义上"失败",有时选择了错误的分支.目前看起来似乎只有在根本不使用邮政运营商时才能避免问题.
更新:同样的问题不仅存在于iOS设备中,而且存在于Safari 6中的Mac OSX和最新的Safari 5中:这些已经过测试,并且发现受到该错误的影响:Mac OS 10.7.4,Safari 5.1.7 Mac OS X 10.8.2,WebKit Nightly r132968:Safari 6.0.1(8536.26.14,537+).有趣的是,这些都没有似乎受到影响:iPad 2的(移动)的Safari 5.1.7,和ipad 1移动Safari 5.1.我已向Apple报告了这些问题但尚未收到任何回复.
更新:该错误已报告为Webkit错误109036.Apple仍未对我的错误报告做出回应,目前(2013年2月)iOS和MacOS上的所有Safari版本仍然受到此问题的影响.
更新2013年2月27日:看来错误已被固定由WebKit团队在这里!这确实是JIT和后期运营商的问题!评论表明,更多的代码可能已经受到了bug的影响,所以现在可能已经修复了更神秘的Heisenbugs!
2013年10月更新:该修复程序最终成为生产代码:至少在iPad2上iOS 7.0.2似乎不再受此错误的影响.但是,我没有检查所有的中间版本,因为我们很久以前就解决了这个问题.
Try-catch块似乎禁用Lion上Safari 6上的JIT编译器直接在try块内部(这段代码在Safari 6.0.1 7536.26.14和OS X Lion上适用于我).
// test function
utility.test = function(){
try {
var a = 0; // counter for index
for (var b = this.getStart(); b !== null; b = b.getNext()) // iterate over all cells
b.$f = a++; // assign index to cell and then increment
}
catch (e) { throw e }
this.$f5 = !1; // random code
};
Run Code Online (Sandbox Code Playgroud)
这至少是当前版本的Google V8的记录行为(请参阅V8上的Google I/O演示文稿),但我不了解Safari.
如果你想为整个脚本禁用它,一个解决方案就是编译你的JS,用一个像burrito这样的工具将每个函数的内容包装在try-catch中.
做好这个可重复的好工作!
| 归档时间: |
|
| 查看次数: |
2452 次 |
| 最近记录: |