2
0
Эх сурвалжийг харах

Open some API and create public API tests for compatibility (#1290)

Marko Lahma 2 жил өмнө
parent
commit
e38849e1c3

+ 0 - 54
Jint.Benchmark/TimeoutBenchmark.cs

@@ -1,54 +0,0 @@
-using BenchmarkDotNet.Attributes;
-using Jint.Constraints;
-
-namespace Jint.Benchmark;
-
-[MemoryDiagnoser]
-public class TimeoutBenchmark
-{
-    private const string Script = "var ret=[],tmp,num=100,i=256;for(var j1=0;j1<i*15;j1++){ret=[];ret.length=i}for(var j2=0;j2<i*10;j2++){ret=new Array(i)}ret=[];for(var j3=0;j3<i;j3++){ret.unshift(j3)}ret=[];for(var j4=0;j4<i;j4++){ret.splice(0,0,j4)}var a=ret.slice();for(var j5=0;j5<i;j5++){tmp=a.shift()}var b=ret.slice();for(var j6=0;j6<i;j6++){tmp=b.splice(0,1)}ret=[];for(var j7=0;j7<i*25;j7++){ret.push(j7)}var c=ret.slice();for(var j8=0;j8<i*25;j8++){tmp=c.pop()}var done = true;";
-
-    private Engine engineTimeout1;
-    private Engine engineTimeout2;
-
-    [GlobalSetup]
-    public void Setup()
-    {
-        engineTimeout1 = new Engine(options =>
-        {
-            options.Constraint(new TimeConstraint(TimeSpan.FromSeconds(5)));
-        });
-
-        engineTimeout2 = new Engine(options =>
-        {
-            options.Constraint(new TimeConstraint2(TimeSpan.FromSeconds(5)));
-        });
-    }
-
-    [Params(10)]
-    public virtual int N { get; set; }
-
-    [Benchmark]
-    public bool Timeout1()
-    {
-        bool done = false;
-        for (var i = 0; i < N; i++)
-        {
-            engineTimeout1.Execute(Script);
-        }
-
-        return done;
-    }
-
-    [Benchmark]
-    public bool Timeout2()
-    {
-        bool done = false;
-        for (var i = 0; i < N; i++)
-        {
-            engineTimeout2.Execute(Script);
-        }
-
-        return done;
-    }
-}

+ 55 - 0
Jint.Tests.PublicInterface/RavenApiUsageTests.cs

@@ -0,0 +1,55 @@
+using Esprima.Ast;
+using Jint.Constraints;
+using Jint.Native.Function;
+
+namespace Jint.Tests.PublicInterface;
+
+/// <summary>
+/// Tests related to functionality that RavenDB needs exposed.
+/// </summary>
+public class RavenApiUsageTests
+{
+    [Fact]
+    public void CanBuildCustomScriptFunctionInstance()
+    {
+        var engine = new Engine();
+
+        var properties = new List<Node>
+        {
+            new Property(PropertyKind.Data, new Identifier("field"), false,
+                new StaticMemberExpression(new Identifier("self"), new Identifier("field"), optional: false), false, false)
+        };
+
+        var functionExp = new FunctionExpression(
+            new Identifier("functionId"),
+            NodeList.Create<Node>(new List<Expression> { new Identifier("self") }),
+            new BlockStatement(NodeList.Create(new List<Statement> { new ReturnStatement(new ObjectExpression(NodeList.Create(properties))) })),
+            generator: false,
+            strict: false,
+            async: false);
+
+        var functionObject = new ScriptFunctionInstance(
+            engine,
+            functionExp,
+            engine.CreateNewDeclarativeEnvironment(),
+            strict: false
+        );
+
+        Assert.NotNull(functionObject);
+    }
+
+    [Fact]
+    public void CanChangeMaxStatementValue()
+    {
+        var engine = new Engine(options => options.MaxStatements(123));
+
+        var constraint = engine.FindConstraint<MaxStatementsConstraint>();
+        Assert.NotNull(constraint);
+
+        var oldMaxStatements = constraint.MaxStatements;
+        constraint.MaxStatements = 321;
+
+        Assert.Equal(123, oldMaxStatements);
+        Assert.Equal(321, constraint.MaxStatements);
+    }
+}

+ 6 - 0
Jint.sln

@@ -17,6 +17,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jint.Tests.Test262", "Jint.
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jint.Tests.PublicInterface", "Jint.Tests.PublicInterface\Jint.Tests.PublicInterface.csproj", "{70198CE9-7DFE-40CA-BBAC-1454C92C4109}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4EB9DD72-0566-4EE4-9550-B064ED2C8332}"
+	ProjectSection(SolutionItems) = preProject
+		README.md = README.md
+		.editorconfig = .editorconfig
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU

+ 19 - 20
Jint/Constraints/CancellationConstraint.cs

@@ -1,32 +1,31 @@
 using Jint.Runtime;
 using System.Threading;
 
-namespace Jint.Constraints
+namespace Jint.Constraints;
+
+public sealed class CancellationConstraint : Constraint
 {
-    public sealed class CancellationConstraint : IConstraint
-    {
-        private CancellationToken _cancellationToken;
+    private CancellationToken _cancellationToken;
 
-        public CancellationConstraint(CancellationToken cancellationToken)
-        {
-            _cancellationToken = cancellationToken;
-        }
+    internal CancellationConstraint(CancellationToken cancellationToken)
+    {
+        _cancellationToken = cancellationToken;
+    }
 
-        public void Check()
+    public override void Check()
+    {
+        if (_cancellationToken.IsCancellationRequested)
         {
-            if (_cancellationToken.IsCancellationRequested)
-            {
-                ExceptionHelper.ThrowExecutionCanceledException();
-            }
+            ExceptionHelper.ThrowExecutionCanceledException();
         }
+    }
 
-        public void Reset(CancellationToken cancellationToken)
-        {
-            _cancellationToken = cancellationToken;
-        }
+    public void Reset(CancellationToken cancellationToken)
+    {
+        _cancellationToken = cancellationToken;
+    }
 
-        public void Reset()
-        {
-        }
+    public override void Reset()
+    {
     }
 }

+ 35 - 37
Jint/Constraints/ConstraintsOptionsExtensions.cs

@@ -1,55 +1,53 @@
 using System.Threading;
 using Jint.Constraints;
 
-namespace Jint
+// ReSharper disable once CheckNamespace
+namespace Jint;
+
+public static class ConstraintsOptionsExtensions
 {
-    public static class ConstraintsOptionsExtensions
+    /// <summary>
+    /// Limits the allowed statement count that can be run as part of the program.
+    /// </summary>
+    public static Options MaxStatements(this Options options, int maxStatements = 0)
     {
-        /// <summary>
-        /// Limits the allowed statement count that can be run as part of the program.
-        /// </summary>
-        public static Options MaxStatements(this Options options, int maxStatements = 0)
-        {
-            options.WithoutConstraint(x => x is MaxStatements);
+        options.WithoutConstraint(x => x is MaxStatementsConstraint);
 
-            if (maxStatements > 0 && maxStatements < int.MaxValue)
-            {
-                options.Constraint(new MaxStatements(maxStatements));
-            }
-            return options;
+        if (maxStatements > 0 && maxStatements < int.MaxValue)
+        {
+            options.Constraint(new MaxStatementsConstraint(maxStatements));
         }
+        return options;
+    }
 
-        public static Options LimitMemory(this Options options, long memoryLimit)
-        {
-            options.WithoutConstraint(x => x is MemoryLimit);
+    public static Options LimitMemory(this Options options, long memoryLimit)
+    {
+        options.WithoutConstraint(x => x is MemoryLimitConstraint);
 
-            if (memoryLimit > 0 && memoryLimit < int.MaxValue)
-            {
-                options.Constraint(new MemoryLimit(memoryLimit));
-            }
-            return options;
+        if (memoryLimit > 0 && memoryLimit < int.MaxValue)
+        {
+            options.Constraint(new MemoryLimitConstraint(memoryLimit));
         }
+        return options;
+    }
 
-        public static Options TimeoutInterval(this Options options, TimeSpan timeoutInterval)
+    public static Options TimeoutInterval(this Options options, TimeSpan timeoutInterval)
+    {
+        if (timeoutInterval > TimeSpan.Zero && timeoutInterval < TimeSpan.MaxValue)
         {
-            options.WithoutConstraint(x => x is TimeConstraint);
-
-            if (timeoutInterval > TimeSpan.Zero && timeoutInterval < TimeSpan.MaxValue)
-            {
-                options.Constraint(new TimeConstraint2(timeoutInterval));
-            }
-            return options;
+            options.Constraint(new TimeConstraint(timeoutInterval));
         }
+        return options;
+    }
 
-        public static Options CancellationToken(this Options options, CancellationToken cancellationToken)
-        {
-            options.WithoutConstraint(x => x is CancellationConstraint);
+    public static Options CancellationToken(this Options options, CancellationToken cancellationToken)
+    {
+        options.WithoutConstraint(x => x is CancellationConstraint);
 
-            if (cancellationToken != default)
-            {
-                options.Constraint(new CancellationConstraint(cancellationToken));
-            }
-            return options;
+        if (cancellationToken != default)
+        {
+            options.Constraint(new CancellationConstraint(cancellationToken));
         }
+        return options;
     }
 }

+ 0 - 28
Jint/Constraints/MaxStatements.cs

@@ -1,28 +0,0 @@
-using Jint.Runtime;
-
-namespace Jint.Constraints
-{
-    internal sealed class MaxStatements : IConstraint
-    {
-        private readonly int _maxStatements;
-        private int _statementsCount;
-
-        public MaxStatements(int maxStatements)
-        {
-            _maxStatements = maxStatements;
-        }
-
-        public void Check()
-        {
-            if (_maxStatements > 0 && _statementsCount++ > _maxStatements)
-            {
-                ExceptionHelper.ThrowStatementsCountOverflowException();
-            }
-        }
-
-        public void Reset()
-        {
-            _statementsCount = 0;
-        }
-    }
-}

+ 31 - 0
Jint/Constraints/MaxStatementsConstraint.cs

@@ -0,0 +1,31 @@
+using Jint.Runtime;
+
+namespace Jint.Constraints;
+
+public sealed class MaxStatementsConstraint : Constraint
+{
+    private int _statementsCount;
+
+    internal MaxStatementsConstraint(int maxStatements)
+    {
+        MaxStatements = maxStatements;
+    }
+
+    /// <summary>
+    /// The maximum configured amount of statements to allow during engine evaluation.
+    /// </summary>
+    public int MaxStatements { get; set; }
+
+    public override void Check()
+    {
+        if (MaxStatements > 0 && _statementsCount++ > MaxStatements)
+        {
+            ExceptionHelper.ThrowStatementsCountOverflowException();
+        }
+    }
+
+    public override void Reset()
+    {
+        _statementsCount = 0;
+    }
+}

+ 0 - 53
Jint/Constraints/MemoryLimit.cs

@@ -1,53 +0,0 @@
-using Jint.Runtime;
-
-namespace Jint.Constraints
-{
-    internal sealed class MemoryLimit : IConstraint
-    {
-        private static readonly Func<long>? GetAllocatedBytesForCurrentThread;
-        private readonly long _memoryLimit;
-        private long _initialMemoryUsage;
-
-        static MemoryLimit()
-        {
-            var methodInfo = typeof(GC).GetMethod("GetAllocatedBytesForCurrentThread");
-
-            if (methodInfo != null)
-            {
-                GetAllocatedBytesForCurrentThread = (Func<long>)Delegate.CreateDelegate(typeof(Func<long>), null, methodInfo);
-            }
-        }
-
-        public MemoryLimit(long memoryLimit)
-        {
-            _memoryLimit = memoryLimit;
-        }
-
-        public void Check()
-        {
-            if (_memoryLimit > 0)
-            {
-                if (GetAllocatedBytesForCurrentThread != null)
-                {
-                    var memoryUsage = GetAllocatedBytesForCurrentThread() - _initialMemoryUsage;
-                    if (memoryUsage > _memoryLimit)
-                    {
-                        ExceptionHelper.ThrowMemoryLimitExceededException($"Script has allocated {memoryUsage} but is limited to {_memoryLimit}");
-                    }
-                }
-                else
-                {
-                    ExceptionHelper.ThrowPlatformNotSupportedException("The current platform doesn't support MemoryLimit.");
-                }
-            }
-        }
-
-        public void Reset()
-        {
-            if (GetAllocatedBytesForCurrentThread != null)
-            {
-                _initialMemoryUsage = GetAllocatedBytesForCurrentThread();
-            }
-        }
-    }
-}

+ 52 - 0
Jint/Constraints/MemoryLimitConstraint.cs

@@ -0,0 +1,52 @@
+using Jint.Runtime;
+
+namespace Jint.Constraints;
+
+public sealed class MemoryLimitConstraint : Constraint
+{
+    private static readonly Func<long>? GetAllocatedBytesForCurrentThread;
+    private readonly long _memoryLimit;
+    private long _initialMemoryUsage;
+
+    static MemoryLimitConstraint()
+    {
+        var methodInfo = typeof(GC).GetMethod("GetAllocatedBytesForCurrentThread");
+
+        if (methodInfo != null)
+        {
+            GetAllocatedBytesForCurrentThread = (Func<long>)Delegate.CreateDelegate(typeof(Func<long>), null, methodInfo);
+        }
+    }
+
+    internal MemoryLimitConstraint(long memoryLimit)
+    {
+        _memoryLimit = memoryLimit;
+    }
+
+    public override void Check()
+    {
+        if (_memoryLimit > 0)
+        {
+            if (GetAllocatedBytesForCurrentThread != null)
+            {
+                var memoryUsage = GetAllocatedBytesForCurrentThread() - _initialMemoryUsage;
+                if (memoryUsage > _memoryLimit)
+                {
+                    ExceptionHelper.ThrowMemoryLimitExceededException($"Script has allocated {memoryUsage} but is limited to {_memoryLimit}");
+                }
+            }
+            else
+            {
+                ExceptionHelper.ThrowPlatformNotSupportedException("The current platform doesn't support MemoryLimit.");
+            }
+        }
+    }
+
+    public override void Reset()
+    {
+        if (GetAllocatedBytesForCurrentThread != null)
+        {
+            _initialMemoryUsage = GetAllocatedBytesForCurrentThread();
+        }
+    }
+}

+ 22 - 19
Jint/Constraints/TimeConstraint.cs

@@ -1,30 +1,33 @@
 using Jint.Runtime;
+using System.Threading;
 
-namespace Jint.Constraints
+namespace Jint.Constraints;
+
+internal sealed class TimeConstraint : Constraint
 {
-    internal sealed class TimeConstraint : IConstraint
-    {
-        private readonly long _maxTicks;
-        private long _timeoutTicks;
+    private readonly TimeSpan _timeout;
+    private CancellationTokenSource? _cts;
 
-        public TimeConstraint(TimeSpan timeout)
-        {
-            _maxTicks = timeout.Ticks;
-        }
+    internal TimeConstraint(TimeSpan timeout)
+    {
+        _timeout = timeout;
+    }
 
-        public void Check()
+    public override void Check()
+    {
+        if (_cts?.IsCancellationRequested == true)
         {
-            if (_timeoutTicks > 0 && _timeoutTicks < DateTime.UtcNow.Ticks)
-            {
-                ExceptionHelper.ThrowTimeoutException();
-            }
+            ExceptionHelper.ThrowTimeoutException();
         }
+    }
 
-        public void Reset()
-        {
-            var timeoutIntervalTicks = _maxTicks;
+    public override void Reset()
+    {
+        _cts?.Dispose();
 
-            _timeoutTicks = timeoutIntervalTicks > 0 ? DateTime.UtcNow.Ticks + timeoutIntervalTicks : 0;
-        }
+        // This cancellation token source is very likely not disposed property, but it only allocates a timer, so not a big deal.
+        // But using the cancellation token source is faster because we do not have to check the current time for each statement,
+        // which means less system calls.
+        _cts = new CancellationTokenSource(_timeout);
     }
 }

+ 0 - 34
Jint/Constraints/TimeConstraint2.cs

@@ -1,34 +0,0 @@
-using Jint.Runtime;
-using System.Threading;
-
-namespace Jint.Constraints
-{
-    internal sealed class TimeConstraint2 : IConstraint
-    {
-        private readonly TimeSpan _timeout;
-        private CancellationTokenSource? _cts;
-
-        public TimeConstraint2(TimeSpan timeout)
-        {
-            _timeout = timeout;
-        }
-
-        public void Check()
-        {
-            if (_cts?.IsCancellationRequested == true)
-            {
-                ExceptionHelper.ThrowTimeoutException();
-            }
-        }
-
-        public void Reset()
-        {
-            _cts?.Dispose();
-
-            // This cancellation token source is very likely not disposed property, but it only allocates a timer, so not a big deal.
-            // But using the cancellation token source is faster because we do not have to check the current time for each statement,
-            // which means less system calls.
-            _cts = new CancellationTokenSource(_timeout);
-        }
-    }
-}

+ 33 - 0
Jint/Engine.Helpers.cs

@@ -0,0 +1,33 @@
+using Jint.Runtime.Environments;
+
+namespace Jint;
+
+/// <summary>
+/// Contains helpers and compatibility shims.
+/// </summary>
+public partial class Engine
+{
+    /// <summary>
+    /// Creates a new declarative environment that has current lexical environment as outer scope.
+    /// </summary>
+    public EnvironmentRecord CreateNewDeclarativeEnvironment()
+    {
+        return JintEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment);
+    }
+
+    /// <summary>
+    /// Return the first constraint that matches the predicate.
+    /// </summary>
+    public T? FindConstraint<T>() where T : Constraint
+    {
+        foreach (var constraint in _constraints)
+        {
+            if (constraint.GetType() == typeof(T))
+            {
+                return (T) constraint;
+            }
+        }
+
+        return null;
+    }
+}

+ 2 - 5
Jint/Engine.cs

@@ -38,7 +38,7 @@ namespace Jint
 
         // cached access
         internal readonly IObjectConverter[]? _objectConverters;
-        internal readonly IConstraint[] _constraints;
+        internal readonly Constraint[] _constraints;
         internal readonly bool _isDebugMode;
         internal bool _isStrict;
         internal readonly IReferenceResolver _referenceResolver;
@@ -412,10 +412,7 @@ namespace Jint
             }
         }
 
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.1
-        /// </summary>
-        public JsValue GetValue(object value)
+        internal JsValue GetValue(object value)
         {
             return GetValue(value, false);
         }

+ 14 - 6
Jint/IConstraint.cs

@@ -1,9 +1,17 @@
-namespace Jint
+namespace Jint;
+
+/// <summary>
+/// A constraint that engine can check for validate during statement execution.
+/// </summary>
+public abstract class Constraint
 {
-    public interface IConstraint
-    {
-        void Reset();
+    /// <summary>
+    /// Called before each statement to check if your requirements are met; if not - throws an exception.
+    /// </summary>
+    public abstract void Check();
 
-        void Check();
-    }
+    /// <summary>
+    /// Called before script is run. Useful when you use an engine object for multiple executions.
+    /// </summary>
+    public abstract void Reset();
 }

+ 2 - 2
Jint/Options.Extensions.cs

@@ -184,7 +184,7 @@ namespace Jint
             return options;
         }
 
-        public static Options Constraint(this Options options, IConstraint constraint)
+        public static Options Constraint(this Options options, Constraint constraint)
         {
             if (constraint != null)
             {
@@ -194,7 +194,7 @@ namespace Jint
             return options;
         }
 
-        public static Options WithoutConstraint(this Options options, Predicate<IConstraint> predicate)
+        public static Options WithoutConstraint(this Options options, Predicate<Constraint> predicate)
         {
             options.Constraints.Constraints.RemoveAll(predicate);
             return options;

+ 1 - 1
Jint/Options.cs

@@ -351,7 +351,7 @@ namespace Jint
         /// <summary>
         /// Registered constraints.
         /// </summary>
-        public List<IConstraint> Constraints { get; } = new();
+        public List<Constraint> Constraints { get; } = new();
 
         /// <summary>
         /// Maximum recursion depth allowed, defaults to -1 (no checks).

+ 9 - 9
README.md

@@ -290,29 +290,29 @@ var engine = new Engine(options => {
 }
 ```
 
-You can also write a custom constraint by implementing the `IConstraint` interface:
+You can also write a custom constraint by deriving from the `Constraint` base class:
 
 ```c#
-public interface IConstraint
+public abstract class Constraint
 {
-    /// Called before a script is run and useful when you us an engine object for multiple executions.
-    void Reset();
+    /// Called before script is run and useful when you use an engine object for multiple executions.
+    public abstract void Reset();
 
-    // Called before each statement to check if your requirements are met.
-    void Check();
+    // Called before each statement to check if your requirements are met; if not - throws an exception.
+    public abstract void Check();
 }
 ```
 
 For example we can write a constraint that stops scripts when the CPU usage gets too high:
 
 ```c#
-class MyCPUConstraint : IConstraint
+class MyCPUConstraint : Constraint
 {
-    public void Reset()
+    public override void Reset()
     {
     }
 
-    public void Check()
+    public override void Check()
     {
         var cpuUsage = GetCPUUsage();