javascript - 在Safari 6中禁用JIT以解决严重的Javascript JIT错误

标签 javascript safari jit safari6

我们发现仅在iOS 5/Safari 6(当时为当前的iPad版本)上出现的Java代码解释中存在严重问题,我们认为这是由于Safari中的Just in Time JS编译器中的严重错误所致。 (有关更多受影响的版本和现在似乎包含修复程序的版本,请参见下面的更新)。

我们最初在库的在线demos中发现了这个问题:演示程序或多或少随机崩溃,但这仅在第二次(甚至更晚)执行同一代码时发生。 IE。如果您只运行部分代码,则一切正常,但是随后的运行会使应用程序崩溃。

有趣的是,在iOS版Chrome浏览器中执行相同的代码并未显示该问题,我们认为这是由于iOS版Chrome浏览器使用的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 

本质上,这是一个简单的for循环,它为链表数据结构中的每个单元分配索引。这里的问题是循环体内的后增量操作。当前计数分配给该字段,并在对表达式求值后进行更新,基本上与首先分配a然后再将其递增1相同。

在前几次测试中,我们在所有测试过的浏览器和Safari中都可以正常工作,然后突然之间,好像计数器变量a先增加,然后分配结果,就像预递增操作一样。

我创建了一个 fiddle ,在这里显示了问题:http://jsfiddle.net/yGuy/L6t5G/

在具有iOS 6和所有更新的iPad 2上运行示例,对于我而言,前两次运行的结果都可以,在第三次相同的运行中,列表中的最后一个元素突然分配的值被一个偏移(输出)当您单击“单击我”按钮时,从“从0到500”更改为“从0到501”)

有趣的是,如果您切换选项卡或稍等片刻,可能会突然发现两次或更多次运行结果正确!似乎Safari有时会重置为JIT缓存。

因此,由于我认为Safari团队可能需要很长时间才能解决此错误(我尚未报告),并且可能存在其他类似的类似错误,这些问题同样难以发现,因此我想在JIT中进行查找了解是否有办法在Safari中禁用JIT功能。当然,这会使我们的代码变慢(这已经非常占用CPU了),但是比崩溃更慢。

更新:
毫不奇怪,受影响的不仅是职位增减运算符,还包括职位增减运算符。毫不奇怪,更令人担忧的是,如果分配了值,这没有什么区别,因此在现有代码中寻找分配是不够的。例如。后面的代码b.$f = (a++ % 2 == 0) ? 1 : 2;(未分配变量值而是仅用于三元运算符条件)在某些时候选择了错误的分支的意义上也“失败”。当前看来,只有完全不使用post运算符,才能避免该问题。

更新:
相同的问题不仅存在于iOS设备中,而且在Safari 6和最新的Safari 5中在Mac OSX上的也存在:
这些已经过测试,发现受此错误影响:
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仍未回复我的错误报告,iOS和MacOS上的所有当前(2013年2月)Safari版本仍受该问题影响。

2013年2月27日更新:
该漏洞似乎已由Webkit团队here修复了! JIT和后期运算符(operator)确实存在问题!注释表明该错误可能影响了更多代码,因此,现在可能已修复了更多神秘的Heisenbugs!

更新2013年10月:
该修复程序最终将其纳入了生产代码:至少在iPad2上的iOS 7.0.2似乎不再受此错误困扰。但是,由于我们很早以前就解决了这个问题,所以我没有检查所有的中间版本。

最佳答案

try catch 块似乎禁用了Lion 6上Safari 6上的JIT编译器,直接针对try块(this code worked for me on Safari 6.0.1 7536.26.14 and 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
};

至少这是当前版本的Google V8(请参见Google I/O presentation on V8)的已记录行为,但是对于Safari我不知道。

如果要在整个脚本中禁用它,一种解决方案是编译JS,以使用burrito之类的工具将每个函数的内容包装在try-catch中。

做这个可复制的好工作!

关于javascript - 在Safari 6中禁用JIT以解决严重的Javascript JIT错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13147026/

相关文章:

javascript - JavaScript 中调用字段的方法

HTML 导入到 HTML 问题

iOS 浏览器抛出网络钓鱼警告

ios - 系统.ExecutionEngineException : Attempting to JIT compile method only in Debug Mode on device (MonoTouch)

java - Java和.Net在JIT上有什么区别

java - 为什么 2 * (i * i) 在 Java 中比 2 * i * i 快?

javascript - 数字、字母 block 的正则表达式模式匹配

javascript - 轻松模拟 Restful 服务的工具

javascript - fc-sun 的全日历背景图像

ipad - ipad/safari 上的盒子阴影