【问题标题】:How to prevent loops in JavaScript that crash the browser or Apps?如何防止 JavaScript 中的循环导致浏览器或应用程序崩溃?
【发布时间】:2014-03-18 03:35:44
【问题描述】:

我正在使用 JavaScript 在 Windows 8.1 App 中创建实时编辑器。几乎完成了,但问题是每当我运行如此糟糕的循环或函数时,它就会自动挂起或退出。

我用一个循环来测试它,例如:(它只是一个例子——用户可以用自己的方式编写它的循环..)

for(i=0;i<=50000;i++)
{
   for(j=0;j<5000;j++){
     $('body').append('hey I am a bug<br>');
   }
}

我知道这是任何应用程序或浏览器处理这种循环的最坏情况。所以在这里我希望如果用户使用这样的循环那么我如何处理它来产生他们的输出?

或者,如果无法针对这种循环保护我的应用程序,如果这对我的应用程序有危险,那么我会提醒用户:

运行这个 sn-p 可能会导致应用崩溃!

如果代码有for(i=0;i&lt;=5000;i++) 之类的内容,我有一个想法可以使用正则表达式检查代码,那么上面的警报将显示,如何为此执行正则表达式?

还可以包含 C# 作为后端

【问题讨论】:

  • 这个循环也以阻塞同步方式运行了50000*50000次,你可以查看stackoverflow.com/questions/4288759/…
  • 是的,这就是为什么我编写这样的代码来检查我的实时编辑器上的错误,并且想要检测那种类型的循环?他们有什么办法吗?
  • 我只是更新问题.. @ArunKillu
  • 如果您的用户可以创建自己的 JavaScript,那么您可能需要依靠浏览器的慢速检测来解决这个问题 - 正则表达式对此并不“感觉正确”,因为总会有这样的情况它无法捕捉。 Firefox 有一个缓慢检测器,并为用户提供中止当前脚本的机会。那么,只要您保存了用户的工作,也许就足够了?不过,我不确定其他浏览器提供什么速度检测。
  • 有点相关,但不是解决方案:stackoverflow.com/q/15676710/472495

标签: c# javascript regex visual-studio-2012 winjs


【解决方案1】:

不幸的是,如果不对编辑后的代码进行深入而复杂的代码分析,您将无法完全防止错误的 JavaScript 导致您的应用程序崩溃。例如,您可以使用一个从 JavaScript 构建抽象语法树的库,如果发现某些模式则不允许执行代码。但是,可能导致无限循环的模式数量很多,因此很难找到,而且可能不够健壮。

for 示例中,您可以将代码修改为:

for(i=0;!timeout() && i<=50000;i++)
{
   for(j=0;!timeout() && j<5000;j++){
     $('body').append('hey I am a bug<br>');
   }
}

我已经“注入”了对您编写的名为timeout 的函数的调用。在那里,它需要能够检测是否应该因为脚本运行时间过长而中止循环。

但是,这可能是用do-while 编写的,因此需要处理这种类型的循环。

在紧密循环中使用 jQuery 的例子,以及修改 DOM 意味着试图将 JavaScript 隔离到 Web Worker 的解决方案会很复杂,因为它不允许直接操作 DOM。它只能发送/接收“字符串”消息。

如果您曾使用 XAML/C# WebView 来托管(和构建)JavaScript 编辑器,您可以考虑使用引发的称为 WebView.LongRunningScriptDetected 的事件。当检测到长时间运行的脚本时会引发它,从而使主机能够在整个应用程序变得无响应并被终止之前终止脚本。

不幸的是,在 WinJS 项目中可用的 x-ms-webview 控件中没有相同的事件。

【讨论】:

  • Actully 我正在使用 html5 应用程序并在 iframe 中创建一个 iframe,我将用户的输出放在他的代码中!,我知道在 HTML5 应用程序中也有一种与 c# 交互的方法..通过使用运行时组件,C# 是否能够处理我运行的长脚本 iframe? @WiredPrairie
  • 正如我所说,如果您为 Web 内容创建了 C# 主机,则可以防止 JavaScript 成为“失控”进程。否则,您需要考虑其他想法——它们不会那么强大。
  • 我喜欢使用 x-web-view 但它不支持 HTML5 的全部功能
  • Web 视图在这两种情况下(XAML 和 WinJS)都只是 Internet Explorer 10+ 的一个完全隔离的实例。在 WinJS 中,x-ms-webview 组件仅在 Windows 8.1 中可用,因此是 IE11。
【解决方案2】:

我有 2 个解决方案:

1.

我的第一个解决方案是定义一个变量 startSeconds=new Date().getSeconds();

然后,使用正则表达式,我将这段代码插入到嵌套循环中。

;if(startSecond < new Date().getSeconds())break;

所以,每次循环运行时,它会做两件事:

检查 startSecond 是否小于当前秒数 new Date().getSeconds();

例如,startSecond 可能是 22。new Date().getSeconds() 可能返回 24。现在,if condition 成功,因此它打破了循环。

大多数情况下,一个非危险循环应该运行大约 2 到 3 秒

for(var i=0;i&lt;30;i++){} 这样的小循环会完全运行,但大循环会运行 3 到 4 秒,这完全没问题。

我的解决方案使用了您自己的 50000*5000 示例,但它不会崩溃!

现场演示:http://jsfiddle.net/nHqUj/4

2.

我的第二个解决方案是定义两个变量startmax

Max 应该是您愿意运行的最大循环数。示例 1000。

然后,使用正则表达式,我将这段代码插入到嵌套循环中。

;start+=1;if(start>max)break;

所以,每次循环运行时,它会做两件事:

  1. start 的值增加1。

  2. 检查 start 是否大于max。如果是,它会打破循环。

此解决方案也使用了您自己的 50000*5000 示例,但不会崩溃!

更新演示:http://jsfiddle.net/nHqUj/3

我正在使用的正则表达式:(?:(for|while|do)\s*\([^\{\}]*\))\s*\{([^\{\}]+)\}

【讨论】:

  • 很棒的方法:),但没有成功,"for(i=0;i&lt;=i;i++){for(j=0;j&lt;=j;j++){$('body').append('hey I am a bug&lt;br&gt;');}}"@DivakarDass 所说的这种循环
  • 您的解决方案也会影响setInterval(function(){...}),因为您的正则表达式string.match(/\{([^\{\}]+)\}/)[1] 仅检测到任何字符串的最内层{...},而不是仅检测for loop!!
  • 还包括你的......另一个以前的答案..因为我更喜欢那个而不是这个..start+=1;if(start&gt;max)break; 并替换你的正则表达式,以便它只得到最里面的for loop
【解决方案3】:

一个想法,但不确定你的编辑器能做什么..

如果你能理解这个循环可能会导致问题(比如如果一个循环超过 200 次,那么它就是一个问题),对于像用户这样的循环,如果你可以将代码更改为下面以提供输出那么它就不会挂起。但坦率地说,不确定它是否适合你。

var j = 0;
var inter = setInterval( function(){
    if( j<5000  ){
      $('#test').append('hey I am a bug<br>');
      ++j;  
    } else {
        clearInterval(inter);
    }
}, 100 );

【讨论】:

  • 你说得对,但我给出了循环的例子,用户代码无论如何,我只是想检查它是否危险,然后我按照其他操作运行用户代码! @A保罗
  • 好的。所以你想了解代码是危险的。我认为某些现有代码或库不可能。您必须编写自己的引擎然后才能做出决定。这是您可以检查的一个链接,它提供了一些答案,例如如何检测浏览器挂起。但是看起来不会对你有太大帮助。 stackoverflow.com/questions/14192758/…
  • @AshishMishra - 抱歉,无法为您提供帮助。但我真的很想看看你如何解决它。如果您最终没有从这里得到任何解决方案,请用您的答案/如何解决它来更新帖子。
  • 不,对不起!! :) 绝对..@A Paul 我认为唯一的解决方案是正则表达式,我不知道如何应用?
  • 嗨,@A Paul 这里是解决方案..我接受作为答案,它是好的还是其中有任何错误的可能性..??
【解决方案4】:

也许在 for 循环周围注入计时器并在第一行检查时间。对每个循环都执行此操作。

正则表达式:/for\([^{]*\)[\s]*{/

例子:

/for\([^{]*\)[\s]*{/.test("for(var i=0; i<length; i++){");
> true

现在,如果您使用 replace 并将 for 包装在一个分组中,您可以获得您想要的结果。

var code = "for(var i=0; i<length; i++){",
    testRegex = /(?:for\([^{]*\)[\s]*{)/g,
    matchReplace = "var timeStarted = new Date().getTime();" +
                   "$1" +
                   "if (new Date().getTime() - timeStarted > maxPossibleTime) {" +
                       "return; // do something here" +
                   "}";

code.replace(textRegex, matchReplace);

【讨论】:

  • maxPossibleTime 是什么?以及我如何定义它,以秒或分钟为单位?
  • 这是你应该定义的一些变量。您应该以毫秒为单位进行定义。
  • @AshishMishra 好吧,你需要调试它。我不知道你的代码是什么样的。它并不适用于所有情况。我不会为你做你所有的工作。我给你一个想法。
【解决方案5】:

你找不到用户试图用一个简单的正则表达式做什么。可以说,用户编写他的代码就像......

for(i=0;i<=5;i++)
{
   for(j=0;j<=5;j++){
     if(j>=3){
         i = i * 5000;
         j = j * 5000;
     }
     $('body').append('hey I am a bug<br>');
   }
}

然后使用简单的正则表达式就无法避免这种情况。因为 i 的值在一段时间后会增加。所以解决问题的最好方法是有一个基准。说,您的应用程序在连续处理 3 分钟后挂起(假设,直到您的应用程序达到 3 分钟的处理时间,它运行良好)。然后,无论用户尝试运行什么代码,您只需在进程之前启动一个计时器,如果该进程花费的时间超过 2.5 分钟,那么您只需在您的应用程序中终止该进程并向用户发出一个弹出窗口说“正在运行这个 sn -p 可能会使应用程序崩溃!'...通过这种方式,您甚至不需要正则表达式或验证用户代码是否错误...

试试这个...可能会有所帮助...干杯!!!

【讨论】:

  • 太好了,提示!但是如何杀死正在运行的特定进程或脚本?它比识别循环更复杂...... @DivakarDass
【解决方案6】:

假设您是在窗口上下文中而不是在工作人员中执行此操作。在每个内部循环中放置一个名为 RocketChair 的函数。这个功能很简单。它增加一个全局计数器并根据全局上限检查该值。当达到上限时,rocketChair 会立即抛出“从危险代码中弹出”。此时,您还可以将您希望保留的任何状态保存到全局状态变量。

将您的整个应用程序封装在一个 try catch 块中,当火箭椅弹出时,您可以像英雄一样拯救一天。

【讨论】:

  • 我明白你所说的,但是我如何检测它是否是一个 for 循环,并启动我的函数`rocketChair`!而且我不想在这里使用任何善良的工人! @cris
  • 使用正则表达式查找 for 循环,while 循环。然后对用户隐藏,将您的函数插入每个循环的第一行(不仅仅是最里面,因为如果它只有条件地执行怎么办?)。如果用户没有使用花括号来指定块,您将需要添加它们,因为您的代码现在至少有两个语句长:一个火箭椅,一个原始(可能是空的)语句。然后在 try catch 中评估修改后的用户代码。
  • regEXP 这样做是为了什么,我尝试这样做,但我找不到好的regEXP,你能否提供检测 for 循环及其值的 regexp "i50000 然后我提醒用户这是危险的或插入function!! @cris
  • 好吧,慢慢来,想想你需要什么。然后,如果您不能自己制作正则表达式,请提出一个新问题。另外,请注意您不需要识别循环条件。计算它们,从调用到 RocketChair 更通用,足以计算大数字。写一些代码然后提出一个新问题是我的建议,谢谢。 @AshishMishra
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-08
相关资源
最近更新 更多