Переглянути джерело

Another, less strict way to be protected from stack overflow caused by recursion

auz34 11 роки тому
батько
коміт
f2297d5f77

+ 32 - 0
Jint.Tests/Runtime/EngineTests.cs

@@ -718,6 +718,38 @@ namespace Jint.Tests.Runtime
             Assert.Equal("funcRoot", exception.CallExpressionReference);
         }
 
+        [Fact]
+        public void ShouldAllowShallowRecursion()
+        {
+            var script = @"var factorial = function(n) {
+                if (n>1) {
+                    return n * factorial(n - 1);
+                }
+            };
+
+            var result = factorial(8);
+            ";
+
+            new Engine(cfg => cfg.MaxRecursionDepth(20)).Execute(script);
+        }
+
+        [Fact]
+        public void ShouldDiscardDeepRecursion()
+        {
+            var script = @"var factorial = function(n) {
+                if (n>1) {
+                    return n * factorial(n - 1);
+                }
+            };
+
+            var result = factorial(38);
+            ";
+
+            Assert.Throws<RecursionDepthOverflowException>(
+                () => new Engine(cfg => cfg.MaxRecursionDepth(20)).Execute(script)
+            );
+        }
+
         [Fact]
         public void ShouldConvertDoubleToStringWithoutLosingPrecision()
         {

+ 1 - 0
Jint/Jint.csproj

@@ -186,6 +186,7 @@
     <Compile Include="Runtime\Interop\TypeReference.cs" />
     <Compile Include="Runtime\Interop\TypeReferencePrototype.cs" />
     <Compile Include="Runtime\JavaScriptException.cs" />
+    <Compile Include="Runtime\RecursionDepthOverflowException.cs" />
     <Compile Include="Runtime\RecursionDiscardedException.cs" />
     <Compile Include="Runtime\References\Reference.cs" />
     <Compile Include="Runtime\StatementInterpreter.cs" />

+ 5 - 0
Jint/Options.cs

@@ -150,6 +150,11 @@ namespace Jint
             return _maxStatements;
         }
 
+        internal int GetMaxRecursionDepth()
+        {
+            return _maxRecursionDepth;
+        }
+
         internal bool IsRecursionAllowed()
         {
             return !_discardRecursion;

+ 6 - 0
Jint/Runtime/ExpressionIntepreter.cs

@@ -807,6 +807,12 @@ namespace Jint.Runtime
                 {
                     throw new RecursionDiscardedException(_engine.CallStack, stackItem);
                 }
+
+                if (_engine.Options.GetMaxRecursionDepth() != 0
+                    && recursionDepth > _engine.Options.GetMaxRecursionDepth())
+                {
+                    throw new RecursionDepthOverflowException(_engine.CallStack, stackItem);
+                }
             }
 
             _engine.CallStack.Push(stackItem);

+ 25 - 0
Jint/Runtime/RecursionDepthOverflowException.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Jint.Native;
+using Jint.Parser.Ast;
+
+namespace Jint.Runtime
+{
+    public class RecursionDepthOverflowException : Exception
+    {
+        public string CallChain { get; private set; }
+
+        public string CallExpressionReference { get; private set; }
+
+        public RecursionDepthOverflowException(Stack<Tuple<CallExpression, JsValue, string>> currentStack, Tuple<CallExpression, JsValue, string> currentExpression)
+            : base("The recursion is forbidden by script host.")
+        {
+            CallExpressionReference = currentExpression.Item3;
+
+            CallChain = string.Join("->", currentStack.Select(t => t.Item3).ToArray().Reverse());
+        }
+    }
+    
+}

+ 5 - 4
Jint/Runtime/RecursionDiscardedException.cs

@@ -1,13 +1,14 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Text;
+
+using Jint.Native;
+using Jint.Parser.Ast;
 
 namespace Jint.Runtime
 {
-    using Jint.Native;
-    using Jint.Parser.Ast;
-    using Jint.Runtime.References;
+    
+    
 
     public class RecursionDiscardedException : Exception 
     {