瀏覽代碼

Refactor options and add MaxArraySize constraint (#923)

Marko Lahma 3 年之前
父節點
當前提交
f5571d0700
共有 29 個文件被更改,包括 940 次插入708 次删除
  1. 0 254
      Jint.Tests/Runtime/EngineTests.cs
  2. 283 0
      Jint.Tests/Runtime/ExecutionConstraintTests.cs
  3. 3 0
      Jint/Constraints/ConstraintsOptionsExtensions.cs
  4. 21 36
      Jint/Engine.cs
  5. 18 2
      Jint/Native/Array/ArrayInstance.cs
  6. 1 1
      Jint/Native/Date/DateConstructor.cs
  7. 8 8
      Jint/Native/Date/DatePrototype.cs
  8. 10 72
      Jint/Native/JsValue.cs
  9. 1 1
      Jint/Native/Number/NumberPrototype.cs
  10. 2 2
      Jint/Native/RegExp/RegExpConstructor.cs
  11. 245 0
      Jint/Options.Extensions.cs
  12. 148 297
      Jint/Options.cs
  13. 37 0
      Jint/Runtime/DefaultReferenceResolver.cs
  14. 2 2
      Jint/Runtime/Descriptors/Specialized/ReflectionDescriptor.cs
  15. 1 2
      Jint/Runtime/ExceptionHelper.cs
  16. 124 0
      Jint/Runtime/Interop/DefaultObjectConverter.cs
  17. 1 1
      Jint/Runtime/Interop/DefaultTypeConverter.cs
  18. 1 1
      Jint/Runtime/Interop/NamespaceReference.cs
  19. 7 7
      Jint/Runtime/Interop/ObjectWrapper.cs
  20. 5 0
      Jint/Runtime/Interop/Reflection/ExtensionMethodCache.cs
  21. 1 1
      Jint/Runtime/Interop/Reflection/IndexerAccessor.cs
  22. 1 1
      Jint/Runtime/Interop/TypeReference.cs
  23. 1 1
      Jint/Runtime/Interop/TypeResolver.cs
  24. 1 1
      Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs
  25. 10 10
      Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs
  26. 4 4
      Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs
  27. 2 2
      Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs
  28. 1 1
      Jint/Runtime/Interpreter/Statements/JintDebuggerStatement.cs
  29. 1 1
      Jint/Runtime/TypeConverter.cs

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

@@ -2,7 +2,6 @@
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Reflection;
 using System.Reflection;
-using System.Threading;
 using Esprima;
 using Esprima;
 using Esprima.Ast;
 using Esprima.Ast;
 using Jint.Native;
 using Jint.Native;
@@ -754,259 +753,6 @@ namespace Jint.Tests.Runtime
             new Engine().Evaluate("debugger");
             new Engine().Evaluate("debugger");
         }
         }
 
 
-        [Fact]
-        public void ShouldThrowStatementCountOverflow()
-        {
-            Assert.Throws<StatementsCountOverflowException>(
-                () => new Engine(cfg => cfg.MaxStatements(100)).Evaluate("while(true);")
-            );
-        }
-
-        [Fact]
-        public void ShouldThrowMemoryLimitExceeded()
-        {
-            Assert.Throws<MemoryLimitExceededException>(
-                () => new Engine(cfg => cfg.LimitMemory(2048)).Evaluate("a=[]; while(true){ a.push(0); }")
-            );
-        }
-
-        [Fact]
-        public void ShouldThrowTimeout()
-        {
-            Assert.Throws<TimeoutException>(
-                () => new Engine(cfg => cfg.TimeoutInterval(new TimeSpan(0, 0, 0, 0, 500))).Evaluate("while(true);")
-            );
-        }
-
-        [Fact]
-        public void ShouldThrowExecutionCanceled()
-        {
-            Assert.Throws<ExecutionCanceledException>(
-                () =>
-                {
-                    using (var tcs = new CancellationTokenSource())
-                    using (var waitHandle = new ManualResetEvent(false))
-                    {
-                        var engine = new Engine(cfg => cfg.CancellationToken(tcs.Token));
-
-                        ThreadPool.QueueUserWorkItem(state =>
-                        {
-                            waitHandle.WaitOne();
-                            tcs.Cancel();
-                        });
-
-                        engine.SetValue("waitHandle", waitHandle);
-                        engine.Evaluate(@"
-                            function sleep(millisecondsTimeout) {
-                                var totalMilliseconds = new Date().getTime() + millisecondsTimeout;
-
-                                while (new Date() < totalMilliseconds) { }
-                            }
-
-                            sleep(100);
-                            waitHandle.Set();
-                            sleep(5000);
-                        ");
-                    }
-                }
-            );
-        }
-
-
-        [Fact]
-        public void CanDiscardRecursion()
-        {
-            var script = @"var factorial = function(n) {
-                if (n>1) {
-                    return n * factorial(n - 1);
-                }
-            };
-
-            var result = factorial(500);
-            ";
-
-            Assert.Throws<RecursionDepthOverflowException>(
-                () => new Engine(cfg => cfg.LimitRecursion()).Execute(script)
-            );
-        }
-
-        [Fact]
-        public void ShouldDiscardHiddenRecursion()
-        {
-            var script = @"var renamedFunc;
-            var exec = function(callback) {
-                renamedFunc = callback;
-                callback();
-            };
-
-            var result = exec(function() {
-                renamedFunc();
-            });
-            ";
-
-            Assert.Throws<RecursionDepthOverflowException>(
-                () => new Engine(cfg => cfg.LimitRecursion()).Execute(script)
-            );
-        }
-
-        [Fact]
-        public void ShouldRecognizeAndDiscardChainedRecursion()
-        {
-            var script = @" var funcRoot, funcA, funcB, funcC, funcD;
-
-            var funcRoot = function() {
-                funcA();
-            };
-
-            var funcA = function() {
-                funcB();
-            };
-
-            var funcB = function() {
-                funcC();
-            };
-
-            var funcC = function() {
-                funcD();
-            };
-
-            var funcD = function() {
-                funcRoot();
-            };
-
-            funcRoot();
-            ";
-
-            Assert.Throws<RecursionDepthOverflowException>(
-                () => new Engine(cfg => cfg.LimitRecursion()).Execute(script)
-            );
-        }
-
-        [Fact]
-        public void ShouldProvideCallChainWhenDiscardRecursion()
-        {
-            var script = @" var funcRoot, funcA, funcB, funcC, funcD;
-
-            var funcRoot = function() {
-                funcA();
-            };
-
-            var funcA = function() {
-                funcB();
-            };
-
-            var funcB = function() {
-                funcC();
-            };
-
-            var funcC = function() {
-                funcD();
-            };
-
-            var funcD = function() {
-                funcRoot();
-            };
-
-            funcRoot();
-            ";
-
-            RecursionDepthOverflowException exception = null;
-
-            try
-            {
-                new Engine(cfg => cfg.LimitRecursion()).Execute(script);
-            }
-            catch (RecursionDepthOverflowException ex)
-            {
-                exception = ex;
-            }
-
-            Assert.NotNull(exception);
-            Assert.Equal("funcRoot->funcA->funcB->funcC->funcD", exception.CallChain);
-            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.LimitRecursion(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.LimitRecursion(20)).Execute(script)
-            );
-        }
-
-        [Fact]
-        public void ShouldAllowRecursionLimitWithoutReferencedName()
-        {
-            const string input = @"(function () {
-                var factorial = function(n) {
-                    if (n>1) {
-                        return n * factorial(n - 1);
-                    }
-                };
-
-                var result = factorial(38);
-            })();
-            ";
-
-            var engine = new Engine(o => o.LimitRecursion(20));
-            Assert.Throws<RecursionDepthOverflowException>(() => engine.Execute(input));
-        }
-
-        [Fact]
-        public void ShouldLimitRecursionWithAllFunctionInstances()
-        {
-            var engine = new Engine(cfg =>
-            {
-                // Limit recursion to 5 invocations
-                cfg.LimitRecursion(5);
-                cfg.Strict();
-            });
-
-            var ex = Assert.Throws<RecursionDepthOverflowException>(() => engine.Evaluate(@"
-var myarr = new Array(5000);
-for (var i = 0; i < myarr.length; i++) {
-    myarr[i] = function(i) {
-        myarr[i + 1](i + 1);
-    }
-}
-
-myarr[0](0);
-"));
-        }
-
-        [Fact]
-        public void ShouldLimitRecursionWithGetters()
-        {
-            const string code = @"var obj = { get test() { return this.test + '2';  } }; obj.test;";
-            var engine = new Engine(cfg => cfg.LimitRecursion(10));
-
-            Assert.Throws<RecursionDepthOverflowException>(() => engine.Evaluate(code));
-        }
-
         [Fact]
         [Fact]
         public void ShouldConvertDoubleToStringWithoutLosingPrecision()
         public void ShouldConvertDoubleToStringWithoutLosingPrecision()
         {
         {

+ 283 - 0
Jint.Tests/Runtime/ExecutionConstraintTests.cs

@@ -0,0 +1,283 @@
+using System;
+using System.Threading;
+using Jint.Runtime;
+using Xunit;
+
+namespace Jint.Tests.Runtime
+{
+    public class ExecutionConstraintTests
+    {
+        [Fact]
+        public void ShouldThrowStatementCountOverflow()
+        {
+            Assert.Throws<StatementsCountOverflowException>(
+                () => new Engine(cfg => cfg.MaxStatements(100)).Evaluate("while(true);")
+            );
+        }
+
+        [Fact]
+        public void ShouldThrowMemoryLimitExceeded()
+        {
+            Assert.Throws<MemoryLimitExceededException>(
+                () => new Engine(cfg => cfg.LimitMemory(2048)).Evaluate("a=[]; while(true){ a.push(0); }")
+            );
+        }
+
+        [Fact]
+        public void ShouldThrowTimeout()
+        {
+            Assert.Throws<TimeoutException>(
+                () => new Engine(cfg => cfg.TimeoutInterval(new TimeSpan(0, 0, 0, 0, 500))).Evaluate("while(true);")
+            );
+        }
+
+        [Fact]
+        public void ShouldThrowExecutionCanceled()
+        {
+            Assert.Throws<ExecutionCanceledException>(
+                () =>
+                {
+                    using (var tcs = new CancellationTokenSource())
+                    using (var waitHandle = new ManualResetEvent(false))
+                    {
+                        var engine = new Engine(cfg => cfg.CancellationToken(tcs.Token));
+
+                        ThreadPool.QueueUserWorkItem(state =>
+                        {
+                            waitHandle.WaitOne();
+                            tcs.Cancel();
+                        });
+
+                        engine.SetValue("waitHandle", waitHandle);
+                        engine.Evaluate(@"
+                            function sleep(millisecondsTimeout) {
+                                var totalMilliseconds = new Date().getTime() + millisecondsTimeout;
+
+                                while (new Date() < totalMilliseconds) { }
+                            }
+
+                            sleep(100);
+                            waitHandle.Set();
+                            sleep(5000);
+                        ");
+                    }
+                }
+            );
+        }
+
+        [Fact]
+        public void CanDiscardRecursion()
+        {
+            var script = @"var factorial = function(n) {
+                if (n>1) {
+                    return n * factorial(n - 1);
+                }
+            };
+
+            var result = factorial(500);
+            ";
+
+            Assert.Throws<RecursionDepthOverflowException>(
+                () => new Engine(cfg => cfg.LimitRecursion()).Execute(script)
+            );
+        }
+
+        [Fact]
+        public void ShouldDiscardHiddenRecursion()
+        {
+            var script = @"var renamedFunc;
+            var exec = function(callback) {
+                renamedFunc = callback;
+                callback();
+            };
+
+            var result = exec(function() {
+                renamedFunc();
+            });
+            ";
+
+            Assert.Throws<RecursionDepthOverflowException>(
+                () => new Engine(cfg => cfg.LimitRecursion()).Execute(script)
+            );
+        }
+
+        [Fact]
+        public void ShouldRecognizeAndDiscardChainedRecursion()
+        {
+            var script = @" var funcRoot, funcA, funcB, funcC, funcD;
+
+            var funcRoot = function() {
+                funcA();
+            };
+
+            var funcA = function() {
+                funcB();
+            };
+
+            var funcB = function() {
+                funcC();
+            };
+
+            var funcC = function() {
+                funcD();
+            };
+
+            var funcD = function() {
+                funcRoot();
+            };
+
+            funcRoot();
+            ";
+
+            Assert.Throws<RecursionDepthOverflowException>(
+                () => new Engine(cfg => cfg.LimitRecursion()).Execute(script)
+            );
+        }
+
+        [Fact]
+        public void ShouldProvideCallChainWhenDiscardRecursion()
+        {
+            var script = @" var funcRoot, funcA, funcB, funcC, funcD;
+
+            var funcRoot = function() {
+                funcA();
+            };
+
+            var funcA = function() {
+                funcB();
+            };
+
+            var funcB = function() {
+                funcC();
+            };
+
+            var funcC = function() {
+                funcD();
+            };
+
+            var funcD = function() {
+                funcRoot();
+            };
+
+            funcRoot();
+            ";
+
+            RecursionDepthOverflowException exception = null;
+
+            try
+            {
+                new Engine(cfg => cfg.LimitRecursion()).Execute(script);
+            }
+            catch (RecursionDepthOverflowException ex)
+            {
+                exception = ex;
+            }
+
+            Assert.NotNull(exception);
+            Assert.Equal("funcRoot->funcA->funcB->funcC->funcD", exception.CallChain);
+            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.LimitRecursion(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.LimitRecursion(20)).Execute(script)
+            );
+        }
+
+        [Fact]
+        public void ShouldAllowRecursionLimitWithoutReferencedName()
+        {
+            const string input = @"(function () {
+                var factorial = function(n) {
+                    if (n>1) {
+                        return n * factorial(n - 1);
+                    }
+                };
+
+                var result = factorial(38);
+            })();
+            ";
+
+            var engine = new Engine(o => o.LimitRecursion(20));
+            Assert.Throws<RecursionDepthOverflowException>(() => engine.Execute(input));
+        }
+
+        [Fact]
+        public void ShouldLimitRecursionWithAllFunctionInstances()
+        {
+            var engine = new Engine(cfg =>
+            {
+                // Limit recursion to 5 invocations
+                cfg.LimitRecursion(5);
+                cfg.Strict();
+            });
+
+            var ex = Assert.Throws<RecursionDepthOverflowException>(() => engine.Evaluate(@"
+var myarr = new Array(5000);
+for (var i = 0; i < myarr.length; i++) {
+    myarr[i] = function(i) {
+        myarr[i + 1](i + 1);
+    }
+}
+
+myarr[0](0);
+"));
+        }
+
+        [Fact]
+        public void ShouldLimitRecursionWithGetters()
+        {
+            const string code = @"var obj = { get test() { return this.test + '2';  } }; obj.test;";
+            var engine = new Engine(cfg => cfg.LimitRecursion(10));
+
+            Assert.Throws<RecursionDepthOverflowException>(() => engine.Evaluate(code));
+        }
+
+        [Fact]
+        public void ShouldLimitArraySizeForConcat()
+        {
+            var engine = new Engine(o => o.MaxStatements(1_000).MaxArraySize(1_000_000));
+            Assert.Throws<MemoryLimitExceededException>(() => engine.Evaluate("for (let a = [1, 2, 3];; a = a.concat(a)) ;"));
+        }
+
+        [Fact]
+        public void ShouldLimitArraySizeForFill()
+        {
+            var engine = new Engine(o => o.MaxStatements(1_000).MaxArraySize(1_000_000));
+            Assert.Throws<MemoryLimitExceededException>(() => engine.Evaluate("var arr = Array(1000000000).fill(new Array(1000000000));"));
+        }
+
+        [Fact]
+        public void ShouldLimitArraySizeForJoin()
+        {
+            var engine = new Engine(o => o.MaxStatements(1_000).MaxArraySize(1_000_000));
+            Assert.Throws<MemoryLimitExceededException>(() => engine.Evaluate("new Array(2147483647).join('*')"));
+        }
+    }
+}

+ 3 - 0
Jint/Constraints/ConstraintsOptionsExtensions.cs

@@ -6,6 +6,9 @@ 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)
         public static Options MaxStatements(this Options options, int maxStatements = 0)
         {
         {
             options.WithoutConstraint(x => x is MaxStatements);
             options.WithoutConstraint(x => x is MaxStatements);

+ 21 - 36
Jint/Engine.cs

@@ -16,6 +16,7 @@ using Jint.Runtime.Debugger;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Environments;
 using Jint.Runtime.Environments;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interop;
+using Jint.Runtime.Interop.Reflection;
 using Jint.Runtime.Interpreter;
 using Jint.Runtime.Interpreter;
 using Jint.Runtime.Interpreter.Expressions;
 using Jint.Runtime.Interpreter.Expressions;
 using Jint.Runtime.References;
 using Jint.Runtime.References;
@@ -40,43 +41,21 @@ namespace Jint
         private DebugHandler _debugHandler;
         private DebugHandler _debugHandler;
 
 
         // cached access
         // cached access
-        private readonly List<IConstraint> _constraints;
+        internal readonly IObjectConverter[] _objectConverters;
+        private readonly IConstraint[] _constraints;
         internal readonly bool _isDebugMode;
         internal readonly bool _isDebugMode;
         internal readonly bool _isStrict;
         internal readonly bool _isStrict;
         internal readonly IReferenceResolver _referenceResolver;
         internal readonly IReferenceResolver _referenceResolver;
         internal readonly ReferencePool _referencePool;
         internal readonly ReferencePool _referencePool;
         internal readonly ArgumentsInstancePool _argumentsInstancePool;
         internal readonly ArgumentsInstancePool _argumentsInstancePool;
         internal readonly JsValueArrayPool _jsValueArrayPool;
         internal readonly JsValueArrayPool _jsValueArrayPool;
+        internal readonly ExtensionMethodCache _extensionMethods;
 
 
         public ITypeConverter ClrTypeConverter { get; internal set; }
         public ITypeConverter ClrTypeConverter { get; internal set; }
 
 
         // cache of types used when resolving CLR type names
         // cache of types used when resolving CLR type names
         internal readonly Dictionary<string, Type> TypeCache = new();
         internal readonly Dictionary<string, Type> TypeCache = new();
 
 
-        internal static Dictionary<Type, Func<Engine, object, JsValue>> TypeMappers = new()
-        {
-            {typeof(bool), (engine, v) => (bool) v ? JsBoolean.True : JsBoolean.False},
-            {typeof(byte), (engine, v) => JsNumber.Create((byte) v)},
-            {typeof(char), (engine, v) => JsString.Create((char) v)},
-            {typeof(DateTime), (engine, v) => engine.Realm.Intrinsics.Date.Construct((DateTime) v)},
-            {typeof(DateTimeOffset), (engine, v) => engine.Realm.Intrinsics.Date.Construct((DateTimeOffset) v)},
-            {typeof(decimal), (engine, v) => (JsValue) (double) (decimal) v},
-            {typeof(double), (engine, v) => (JsValue) (double) v},
-            {typeof(Int16), (engine, v) => JsNumber.Create((Int16) v)},
-            {typeof(Int32), (engine, v) => JsNumber.Create((Int32) v)},
-            {typeof(Int64), (engine, v) => (JsValue) (Int64) v},
-            {typeof(SByte), (engine, v) => JsNumber.Create((SByte) v)},
-            {typeof(Single), (engine, v) => (JsValue) (Single) v},
-            {typeof(string), (engine, v) => JsString.Create((string) v)},
-            {typeof(UInt16), (engine, v) => JsNumber.Create((UInt16) v)},
-            {typeof(UInt32), (engine, v) => JsNumber.Create((UInt32) v)},
-            {typeof(UInt64), (engine, v) => JsNumber.Create((UInt64) v)},
-            {
-                typeof(System.Text.RegularExpressions.Regex),
-                (engine, v) => engine.Realm.Intrinsics.RegExp.Construct((System.Text.RegularExpressions.Regex) v, "")
-            }
-        };
-
         // shared frozen version
         // shared frozen version
         internal readonly PropertyDescriptor _callerCalleeArgumentsThrowerConfigurable;
         internal readonly PropertyDescriptor _callerCalleeArgumentsThrowerConfigurable;
         internal readonly PropertyDescriptor _callerCalleeArgumentsThrowerNonConfigurable;
         internal readonly PropertyDescriptor _callerCalleeArgumentsThrowerNonConfigurable;
@@ -130,11 +109,17 @@ namespace Jint
             Reset();
             Reset();
 
 
             // gather some options as fields for faster checks
             // gather some options as fields for faster checks
-            _isDebugMode = Options.IsDebugMode;
-            _isStrict = Options.IsStrict;
-            _constraints = Options._Constraints;
+            _isDebugMode = Options.Debugger.Enabled;
+            _isStrict = Options.Strict;
+
+            _objectConverters = Options.Interop.ObjectConverters.Count > 0
+                ? Options.Interop.ObjectConverters.ToArray()
+                : null;
+
+            _constraints = Options.Constraints.Constraints.ToArray();
             _referenceResolver = Options.ReferenceResolver;
             _referenceResolver = Options.ReferenceResolver;
-            CallStack = new JintCallStack(Options.MaxRecursionDepth >= 0);
+            _extensionMethods = ExtensionMethodCache.Build(Options.Interop.ExtensionMethodTypes);
+            CallStack = new JintCallStack(Options.Constraints.MaxRecursionDepth >= 0);
 
 
             _referencePool = new ReferencePool();
             _referencePool = new ReferencePool();
             _argumentsInstancePool = new ArgumentsInstancePool(this);
             _argumentsInstancePool = new ArgumentsInstancePool(this);
@@ -145,7 +130,7 @@ namespace Jint
 
 
         private void Reset()
         private void Reset()
         {
         {
-            _host = Options._hostFactory(this);
+            _host = Options.Host.Factory(this);
             _host.Initialize(this);
             _host.Initialize(this);
         }
         }
 
 
@@ -249,9 +234,9 @@ namespace Jint
         /// </summary>
         /// </summary>
         public void ResetConstraints()
         public void ResetConstraints()
         {
         {
-            for (var i = 0; i < _constraints.Count; i++)
+            foreach (var constraint in _constraints)
             {
             {
-                _constraints[i].Reset();
+                constraint.Reset();
             }
             }
         }
         }
 
 
@@ -381,9 +366,9 @@ namespace Jint
         internal void RunBeforeExecuteStatementChecks(Statement statement)
         internal void RunBeforeExecuteStatementChecks(Statement statement)
         {
         {
             // Avoid allocating the enumerator because we run this loop very often.
             // Avoid allocating the enumerator because we run this loop very often.
-            for (var i = 0; i < _constraints.Count; i++)
+            foreach (var constraint in _constraints)
             {
             {
-                _constraints[i].Check();
+                constraint.Check();
             }
             }
 
 
             if (_isDebugMode)
             if (_isDebugMode)
@@ -1249,7 +1234,7 @@ namespace Jint
             var callStackElement = new CallStackElement(functionInstance, expression, ExecutionContext);
             var callStackElement = new CallStackElement(functionInstance, expression, ExecutionContext);
             var recursionDepth = CallStack.Push(callStackElement);
             var recursionDepth = CallStack.Push(callStackElement);
 
 
-            if (recursionDepth > Options.MaxRecursionDepth)
+            if (recursionDepth > Options.Constraints.MaxRecursionDepth)
             {
             {
                 // pop the current element as it was never reached
                 // pop the current element as it was never reached
                 CallStack.Pop();
                 CallStack.Pop();
@@ -1272,7 +1257,7 @@ namespace Jint
             var callStackElement = new CallStackElement(functionInstance, expression, ExecutionContext);
             var callStackElement = new CallStackElement(functionInstance, expression, ExecutionContext);
             var recursionDepth = CallStack.Push(callStackElement);
             var recursionDepth = CallStack.Push(callStackElement);
 
 
-            if (recursionDepth > Options.MaxRecursionDepth)
+            if (recursionDepth > Options.Constraints.MaxRecursionDepth)
             {
             {
                 // pop the current element as it was never reached
                 // pop the current element as it was never reached
                 CallStack.Pop();
                 CallStack.Pop();

+ 18 - 2
Jint/Native/Array/ArrayInstance.cs

@@ -12,8 +12,7 @@ namespace Jint.Native.Array
     {
     {
         internal PropertyDescriptor _length;
         internal PropertyDescriptor _length;
 
 
-        private const int MaxDenseArrayLength = 1024 * 10;
-        private const ulong MaxArrayLength = 4294967295;
+        private const int MaxDenseArrayLength = 10_000_000;
 
 
         // we have dense and sparse, we usually can start with dense and fall back to sparse when necessary
         // we have dense and sparse, we usually can start with dense and fall back to sparse when necessary
         internal PropertyDescriptor[] _dense;
         internal PropertyDescriptor[] _dense;
@@ -21,6 +20,11 @@ namespace Jint.Native.Array
 
 
         public ArrayInstance(Engine engine, uint capacity = 0) : base(engine, ObjectClass.Array)
         public ArrayInstance(Engine engine, uint capacity = 0) : base(engine, ObjectClass.Array)
         {
         {
+            if (capacity > engine.Options.Constraints.MaxArraySize)
+            {
+                ThrowMaximumArraySizeReachedException(engine, capacity);
+            }
+
             if (capacity < MaxDenseArrayLength)
             if (capacity < MaxDenseArrayLength)
             {
             {
                 _dense = capacity > 0 ? new PropertyDescriptor[capacity] : System.Array.Empty<PropertyDescriptor>();
                 _dense = capacity > 0 ? new PropertyDescriptor[capacity] : System.Array.Empty<PropertyDescriptor>();
@@ -664,6 +668,11 @@ namespace Jint.Native.Array
                 return;
                 return;
             }
             }
 
 
+            if (capacity > _engine.Options.Constraints.MaxArraySize)
+            {
+                ThrowMaximumArraySizeReachedException(_engine, capacity);
+            }
+
             // need to grow
             // need to grow
             var newArray = new PropertyDescriptor[capacity];
             var newArray = new PropertyDescriptor[capacity];
             System.Array.Copy(_dense, newArray, _dense.Length);
             System.Array.Copy(_dense, newArray, _dense.Length);
@@ -905,5 +914,12 @@ namespace Jint.Native.Array
             // debugger can make things hard when evaluates computed values
             // debugger can make things hard when evaluates computed values
             return "(" + (_length?._value.AsNumber() ?? 0) + ")[]";
             return "(" + (_length?._value.AsNumber() ?? 0) + ")[]";
         }
         }
+
+        private static void ThrowMaximumArraySizeReachedException(Engine engine, uint capacity)
+        {
+            ExceptionHelper.ThrowMemoryLimitExceededException(
+                $"The array size {capacity} is larger than maximum allowed ({engine.Options.Constraints.MaxArraySize})"
+            );
+        }
     }
     }
 }
 }

+ 1 - 1
Jint/Native/Date/DateConstructor.cs

@@ -89,7 +89,7 @@ namespace Jint.Native.Date
             {
             {
                 if (!DateTime.TryParseExact(date, SecondaryFormats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result))
                 if (!DateTime.TryParseExact(date, SecondaryFormats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result))
                 {
                 {
-                    if (!DateTime.TryParse(date, Engine.Options._Culture, DateTimeStyles.AdjustToUniversal, out result))
+                    if (!DateTime.TryParse(date, Engine.Options.Culture, DateTimeStyles.AdjustToUniversal, out result))
                     {
                     {
                         if (!DateTime.TryParse(date, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result))
                         if (!DateTime.TryParse(date, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out result))
                         {
                         {

+ 8 - 8
Jint/Native/Date/DatePrototype.cs

@@ -158,7 +158,7 @@ namespace Jint.Native.Date
             if (double.IsNaN(dateInstance.PrimitiveValue))
             if (double.IsNaN(dateInstance.PrimitiveValue))
                 return "Invalid Date";
                 return "Invalid Date";
 
 
-            var t = ToLocalTime(dateInstance.ToDateTime(), Engine.Options._LocalTimeZone);
+            var t = ToLocalTime(dateInstance.ToDateTime(), Engine.Options.TimeZone);
             return t.ToString("ddd MMM dd yyyy HH:mm:ss ", CultureInfo.InvariantCulture) + TimeZoneString(t);
             return t.ToString("ddd MMM dd yyyy HH:mm:ss ", CultureInfo.InvariantCulture) + TimeZoneString(t);
         }
         }
 
 
@@ -169,7 +169,7 @@ namespace Jint.Native.Date
             if (double.IsNaN(dateInstance.PrimitiveValue))
             if (double.IsNaN(dateInstance.PrimitiveValue))
                 return "Invalid Date";
                 return "Invalid Date";
 
 
-            return ToLocalTime(dateInstance.ToDateTime(), Engine.Options._LocalTimeZone).ToString("ddd MMM dd yyyy", CultureInfo.InvariantCulture);
+            return ToLocalTime(dateInstance.ToDateTime(), Engine.Options.TimeZone).ToString("ddd MMM dd yyyy", CultureInfo.InvariantCulture);
         }
         }
 
 
         private JsValue ToTimeString(JsValue thisObj, JsValue[] arguments)
         private JsValue ToTimeString(JsValue thisObj, JsValue[] arguments)
@@ -179,7 +179,7 @@ namespace Jint.Native.Date
             if (double.IsNaN(dateInstance.PrimitiveValue))
             if (double.IsNaN(dateInstance.PrimitiveValue))
                 return "Invalid Date";
                 return "Invalid Date";
 
 
-            var t = ToLocalTime(dateInstance.ToDateTime(), Engine.Options._LocalTimeZone);
+            var t = ToLocalTime(dateInstance.ToDateTime(), Engine.Options.TimeZone);
 
 
             var timeString = t.ToString("HH:mm:ss ", CultureInfo.InvariantCulture);
             var timeString = t.ToString("HH:mm:ss ", CultureInfo.InvariantCulture);
             var timeZoneString = TimeZoneString(t);
             var timeZoneString = TimeZoneString(t);
@@ -198,7 +198,7 @@ namespace Jint.Native.Date
             if (double.IsNaN(dateInstance.PrimitiveValue))
             if (double.IsNaN(dateInstance.PrimitiveValue))
                 return "Invalid Date";
                 return "Invalid Date";
 
 
-            return ToLocalTime(dateInstance.ToDateTime(), Engine.Options._LocalTimeZone).ToString("F", Engine.Options._Culture);
+            return ToLocalTime(dateInstance.ToDateTime(), Engine.Options.TimeZone).ToString("F", Engine.Options.Culture);
         }
         }
 
 
         private JsValue ToLocaleDateString(JsValue thisObj, JsValue[] arguments)
         private JsValue ToLocaleDateString(JsValue thisObj, JsValue[] arguments)
@@ -208,7 +208,7 @@ namespace Jint.Native.Date
             if (double.IsNaN(dateInstance.PrimitiveValue))
             if (double.IsNaN(dateInstance.PrimitiveValue))
                 return "Invalid Date";
                 return "Invalid Date";
 
 
-            return ToLocalTime(dateInstance.ToDateTime(), Engine.Options._LocalTimeZone).ToString("D", Engine.Options._Culture);
+            return ToLocalTime(dateInstance.ToDateTime(), Engine.Options.TimeZone).ToString("D", Engine.Options.Culture);
         }
         }
 
 
         private JsValue ToLocaleTimeString(JsValue thisObj, JsValue[] arguments)
         private JsValue ToLocaleTimeString(JsValue thisObj, JsValue[] arguments)
@@ -218,7 +218,7 @@ namespace Jint.Native.Date
             if (double.IsNaN(dateInstance.PrimitiveValue))
             if (double.IsNaN(dateInstance.PrimitiveValue))
                 return "Invalid Date";
                 return "Invalid Date";
 
 
-            return ToLocalTime(dateInstance.ToDateTime(), Engine.Options._LocalTimeZone).ToString("T", Engine.Options._Culture);
+            return ToLocalTime(dateInstance.ToDateTime(), Engine.Options.TimeZone).ToString("T", Engine.Options.Culture);
         }
         }
 
 
         private JsValue GetTime(JsValue thisObj, JsValue[] arguments)
         private JsValue GetTime(JsValue thisObj, JsValue[] arguments)
@@ -968,7 +968,7 @@ namespace Jint.Native.Date
             return (Day(t) + 4)%7;
             return (Day(t) + 4)%7;
         }
         }
 
 
-        public long LocalTza => (long) Engine.Options._LocalTimeZone.BaseUtcOffset.TotalMilliseconds;
+        public long LocalTza => (long) Engine.Options.TimeZone.BaseUtcOffset.TotalMilliseconds;
 
 
         public double DaylightSavingTa(double t)
         public double DaylightSavingTa(double t)
         {
         {
@@ -998,7 +998,7 @@ namespace Jint.Native.Date
 
 
             var dateTime = new DateTime(year, 1, 1).AddMilliseconds(timeInYear);
             var dateTime = new DateTime(year, 1, 1).AddMilliseconds(timeInYear);
 
 
-            return Engine.Options._LocalTimeZone.IsDaylightSavingTime(dateTime) ? MsPerHour : 0;
+            return Engine.Options.TimeZone.IsDaylightSavingTime(dateTime) ? MsPerHour : 0;
         }
         }
 
 
         private static DateTimeOffset ToLocalTime(DateTime t, TimeZoneInfo timeZone)
         private static DateTimeOffset ToLocalTime(DateTime t, TimeZoneInfo timeZone)

+ 10 - 72
Jint/Native/JsValue.cs

@@ -1,9 +1,7 @@
 using System;
 using System;
-using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Diagnostics.Contracts;
 using System.Diagnostics.Contracts;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
-using System.Threading;
 using Jint.Native.Array;
 using Jint.Native.Array;
 using Jint.Native.Date;
 using Jint.Native.Date;
 using Jint.Native.Iterator;
 using Jint.Native.Iterator;
@@ -13,8 +11,6 @@ using Jint.Native.Promise;
 using Jint.Native.RegExp;
 using Jint.Native.RegExp;
 using Jint.Native.Symbol;
 using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime;
-using Jint.Runtime.Descriptors;
-using Jint.Runtime.Interop;
 
 
 namespace Jint.Native
 namespace Jint.Native
 {
 {
@@ -292,7 +288,7 @@ namespace Jint.Native
         /// </summary>
         /// </summary>
         public static JsValue FromObject(Engine engine, object value)
         public static JsValue FromObject(Engine engine, object value)
         {
         {
-            if (value == null)
+            if (value is null)
             {
             {
                 return Null;
                 return Null;
             }
             }
@@ -302,81 +298,23 @@ namespace Jint.Native
                 return jsValue;
                 return jsValue;
             }
             }
 
 
-            var converters = engine.Options._ObjectConverters;
-            var convertersCount = converters.Count;
-            for (var i = 0; i < convertersCount; i++)
+            if (engine._objectConverters != null)
             {
             {
-                var converter = converters[i];
-                if (converter.TryConvert(engine, value, out var result))
+                foreach (var converter in engine._objectConverters)
                 {
                 {
-                    return result;
-                }
-            }
-
-            var valueType = value.GetType();
-
-            var typeMappers = Engine.TypeMappers;
-
-            if (typeMappers.TryGetValue(valueType, out var typeMapper))
-            {
-                return typeMapper(engine, value);
-            }
-
-            if (value is System.Array a)
-            {
-                // racy, we don't care, worst case we'll catch up later
-                Interlocked.CompareExchange(ref Engine.TypeMappers,
-                    new Dictionary<Type, Func<Engine, object, JsValue>>(typeMappers)
+                    if (converter.TryConvert(engine, value, out var result))
                     {
                     {
-                        [valueType] = Convert
-                    }, typeMappers);
-
-                return Convert(engine, a);
-            }
-
-            if (value is Delegate d)
-            {
-                return new DelegateWrapper(engine, d);
-            }
-
-            Type t = value.GetType();
-            if (t.IsEnum)
-            {
-                Type ut = Enum.GetUnderlyingType(t);
-
-                if (ut == typeof(ulong))
-                    return JsNumber.Create(System.Convert.ToDouble(value));
-
-                if (ut == typeof(uint) || ut == typeof(long))
-                    return JsNumber.Create(System.Convert.ToInt64(value));
-
-                return JsNumber.Create(System.Convert.ToInt32(value));
+                        return result;
+                    }
+                }
             }
             }
 
 
-            // if no known type could be guessed, wrap it as an ObjectInstance
-            var h = engine.Options._WrapObjectHandler;
-            var o = h?.Invoke(engine, value) ?? new ObjectWrapper(engine, value);
-            return o;
-        }
-
-        private static JsValue Convert(Engine e, object v)
-        {
-            var array = (System.Array) v;
-            var arrayLength = (uint) array.Length;
-
-            var jsArray = new ArrayInstance(e, arrayLength);
-            jsArray._prototype = e.Realm.Intrinsics.Array.PrototypeObject;
-
-            for (uint i = 0; i < arrayLength; ++i)
+            if (DefaultObjectConverter.TryConvert(engine, value, out var defaultConversion))
             {
             {
-                var jsItem = FromObject(e, array.GetValue(i));
-                jsArray.WriteArrayValue(i, new PropertyDescriptor(jsItem, PropertyFlag.ConfigurableEnumerableWritable));
+                return defaultConversion;
             }
             }
 
 
-            jsArray.SetOwnProperty(CommonProperties.Length,
-                new PropertyDescriptor(arrayLength, PropertyFlag.OnlyWritable));
-
-            return jsArray;
+            return null;
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 1 - 1
Jint/Native/Number/NumberPrototype.cs

@@ -81,7 +81,7 @@ namespace Jint.Native.Number
                 return "-Infinity";
                 return "-Infinity";
             }
             }
 
 
-            return m.ToString("n", Engine.Options._Culture);
+            return m.ToString("n", Engine.Options.Culture);
         }
         }
 
 
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)
         private JsValue ValueOf(JsValue thisObj, JsValue[] arguments)

+ 2 - 2
Jint/Native/RegExp/RegExpConstructor.cs

@@ -108,7 +108,7 @@ namespace Jint.Native.RegExp
                 // seems valid
                 // seems valid
                 r.Value = scanner.TestRegExp(p, f);
                 r.Value = scanner.TestRegExp(p, f);
 
 
-                var timeout = _engine.Options._RegexTimeoutInterval;
+                var timeout = _engine.Options.Constraints.RegexTimeout;
                 if (timeout.Ticks > 0)
                 if (timeout.Ticks > 0)
                 {
                 {
                     r.Value = r.Value != null ? new Regex(r.Value.ToString(), r.Value.Options, timeout) : null;
                     r.Value = r.Value != null ? new Regex(r.Value.ToString(), r.Value.Options, timeout) : null;
@@ -144,7 +144,7 @@ namespace Jint.Native.RegExp
             r.Flags = flags;
             r.Flags = flags;
             r.Source = regExp?.ToString();
             r.Source = regExp?.ToString();
 
 
-            var timeout = _engine.Options._RegexTimeoutInterval;
+            var timeout = _engine.Options.Constraints.RegexTimeout;
             if (timeout.Ticks > 0)
             if (timeout.Ticks > 0)
             {
             {
                 r.Value = regExp != null ? new Regex(regExp.ToString(), regExp.Options, timeout) : null;
                 r.Value = regExp != null ? new Regex(regExp.ToString(), regExp.Options, timeout) : null;

+ 245 - 0
Jint/Options.Extensions.cs

@@ -0,0 +1,245 @@
+#nullable enable
+
+using System;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using Jint.Native;
+using Jint.Runtime;
+using Jint.Runtime.Debugger;
+using Jint.Runtime.Interop;
+
+namespace Jint
+{
+    /// <summary>
+    /// Compatibility layer to allow fluent syntax against options object.
+    /// </summary>
+    public static class OptionsExtensions
+    {
+        /// <summary>
+        /// Run the script in strict mode.
+        /// </summary>
+        public static Options Strict(this Options options, bool strict = true)
+        {
+            options.Strict = strict;
+            return options;
+        }
+
+        /// <summary>
+        /// Selects the handling for script <code>debugger</code> statements.
+        /// </summary>
+        /// <remarks>
+        /// The <c>debugger</c> statement can either be ignored (default) trigger debugging at CLR level (e.g. Visual Studio),
+        /// or trigger a break in Jint's DebugHandler.
+        /// </remarks>
+        public static Options DebuggerStatementHandling(this Options options,
+            DebuggerStatementHandling debuggerStatementHandling)
+        {
+            options.Debugger.StatementHandling = debuggerStatementHandling;
+            return options;
+        }
+
+        /// <summary>
+        /// Allow to run the script in debug mode.
+        /// </summary>
+        public static Options DebugMode(this Options options, bool debugMode = true)
+        {
+            options.Debugger.Enabled = debugMode;
+            return options;
+        }
+
+        /// <summary>
+        /// Adds a <see cref="IObjectConverter"/> instance to convert CLR types to <see cref="JsValue"/>
+        /// </summary>
+        public static Options AddObjectConverter<T>(this Options options) where T : IObjectConverter, new()
+        {
+            return AddObjectConverter(options, new T());
+        }
+
+        /// <summary>
+        /// Adds a <see cref="IObjectConverter"/> instance to convert CLR types to <see cref="JsValue"/>
+        /// </summary>
+        public static Options AddObjectConverter(this Options options, IObjectConverter objectConverter)
+        {
+            options.Interop.ObjectConverters.Add(objectConverter);
+            return options;
+        }
+
+        /// <summary>
+        /// Sets maximum allowed depth of recursion.
+        /// </summary>
+        /// <param name="maxRecursionDepth">
+        /// The allowed depth.
+        /// a) In case max depth is zero no recursion is allowed.
+        /// b) In case max depth is equal to n it means that in one scope function can be called no more than n times.
+        /// </param>
+        /// <returns>Options instance for fluent syntax</returns>
+        public static Options LimitRecursion(this Options options, int maxRecursionDepth = 0)
+        {
+            options.Constraints.MaxRecursionDepth = maxRecursionDepth;
+            return options;
+        }
+
+        public static Options Culture(this Options options, CultureInfo cultureInfo)
+        {
+            options.Culture = cultureInfo;
+            return options;
+        }
+
+        public static Options LocalTimeZone(this Options options, TimeZoneInfo timeZoneInfo)
+        {
+            options.TimeZone = timeZoneInfo;
+            return options;
+        }
+
+        public static Options AddExtensionMethods(this Options options, params Type[] types)
+        {
+            options.Interop.ExtensionMethodTypes.AddRange(types);
+            return options;
+        }
+
+        /// <summary>
+        /// If no known type could be guessed, objects are normally wrapped as an
+        /// ObjectInstance using class ObjectWrapper. This function can be used to
+        /// register a handler for a customized handling.
+        /// </summary>
+        public static Options SetWrapObjectHandler(this Options options, WrapObjectDelegate wrapObjectHandler)
+        {
+            options.Interop.WrapObjectHandler = wrapObjectHandler;
+            return options;
+        }
+
+        /// <summary>
+        /// Sets the type converter to use.
+        /// </summary>
+        public static Options SetTypeConverter(this Options options, Func<Engine, ITypeConverter> typeConverterFactory)
+        {
+            options._configurations.Add(engine => engine.ClrTypeConverter = typeConverterFactory(engine));
+            return options;
+        }
+
+        /// <summary>
+        /// Registers a delegate that is called when CLR members are invoked. This allows
+        /// to change what values are returned for specific CLR objects, or if any value
+        /// is returned at all.
+        /// </summary>
+        /// <param name="accessor">
+        /// The delegate to invoke for each CLR member. If the delegate
+        /// returns <c>null</c>, the standard evaluation is performed.
+        /// </param>
+        public static Options SetMemberAccessor(this Options options, MemberAccessorDelegate accessor)
+        {
+            options.Interop.MemberAccessor = accessor;
+            return options;
+        }
+
+        /// <summary>
+        /// Allows scripts to call CLR types directly like <example>System.IO.File</example>
+        /// </summary>
+        public static Options AllowClr(this Options options, params Assembly[] assemblies)
+        {
+            options.Interop.Enabled = true;
+            options.Interop.AllowedAssemblies.AddRange(assemblies);
+            options.Interop.AllowedAssemblies = options.Interop.AllowedAssemblies.Distinct().ToList();
+            return options;
+        }
+
+        public static Options AllowClrWrite(this Options options, bool allow = true)
+        {
+            options.Interop.AllowWrite = allow;
+            return options;
+        }
+
+        public static Options AllowOperatorOverloading(this Options options, bool allow = true)
+        {
+            options.Interop.OperatorOverloadingAllowed = allow;
+            return options;
+        }
+
+        /// <summary>
+        /// Exceptions thrown from CLR code are converted to JavaScript errors and
+        /// can be used in at try/catch statement. By default these exceptions are bubbled
+        /// to the CLR host and interrupt the script execution.
+        /// </summary>
+        public static Options CatchClrExceptions(this Options options)
+        {
+            CatchClrExceptions(options, _ => true);
+            return options;
+        }
+
+        /// <summary>
+        /// Exceptions that thrown from CLR code are converted to JavaScript errors and
+        /// can be used in at try/catch statement. By default these exceptions are bubbled
+        /// to the CLR host and interrupt the script execution.
+        /// </summary>
+        public static Options CatchClrExceptions(this Options options, ExceptionHandlerDelegate handler)
+        {
+            options.Interop.ExceptionHandler = handler;
+            return options;
+        }
+
+        public static Options Constraint(this Options options, IConstraint constraint)
+        {
+            if (constraint != null)
+            {
+                options.Constraints.Constraints.Add(constraint);
+            }
+
+            return options;
+        }
+
+        public static Options WithoutConstraint(this Options options, Predicate<IConstraint> predicate)
+        {
+            options.Constraints.Constraints.RemoveAll(predicate);
+            return options;
+        }
+
+        public static Options RegexTimeoutInterval(this Options options, TimeSpan regexTimeoutInterval)
+        {
+            options.Constraints.RegexTimeout = regexTimeoutInterval;
+            return options;
+        }
+
+
+        public static Options MaxArraySize(this Options options, uint maxSize)
+        {
+            options.Constraints.MaxArraySize = maxSize;
+            return options;
+        }
+
+
+        public static Options SetReferencesResolver(this Options options, IReferenceResolver resolver)
+        {
+            options.ReferenceResolver = resolver;
+            return options;
+        }
+
+        public static Options SetTypeResolver(this Options options, TypeResolver resolver)
+        {
+            options.Interop.TypeResolver = resolver;
+            return options;
+        }
+
+        /// <summary>
+        /// Registers some custom logic to apply on an <see cref="Engine"/> instance when the options
+        /// are loaded.
+        /// </summary>
+        /// <param name="configuration">The action to register.</param>
+        public static Options Configure(this Options options, Action<Engine> configuration)
+        {
+            options._configurations.Add(configuration);
+            return options;
+        }
+
+        /// <summary>
+        /// Allows to configure how the host is constructed.
+        /// </summary>
+        /// <remarks>
+        /// Passed Engine instance is still in construction and should not be used during call stage.
+        /// </remarks>
+        public static void UseHostFactory<T>(this Options options, Func<Engine, T> factory) where T : Host
+        {
+            options.Host.Factory = factory;
+        }
+    }
+}

+ 148 - 297
Jint/Options.cs

@@ -1,4 +1,6 @@
-using System;
+#nullable enable
+
+using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Dynamic;
 using System.Dynamic;
 using System.Globalization;
 using System.Globalization;
@@ -10,94 +12,96 @@ using Jint.Runtime;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Debugger;
 using Jint.Runtime.Debugger;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors;
-using Jint.Runtime.Interop.Reflection;
-using Jint.Runtime.References;
 
 
 namespace Jint
 namespace Jint
 {
 {
-    public delegate JsValue MemberAccessorDelegate(Engine engine, object target, string member);
+    public delegate JsValue? MemberAccessorDelegate(Engine engine, object target, string member);
+
+    public delegate ObjectInstance? WrapObjectDelegate(Engine engine, object target);
+
+    public delegate bool ExceptionHandlerDelegate(Exception exception);
 
 
-    public sealed class Options
+    public class Options
     {
     {
-        private readonly List<IConstraint> _constraints = new();
-        private bool _strict;
-        private DebuggerStatementHandling _debuggerStatementHandling;
-        private bool _allowClr;
-        private bool _allowClrWrite = true;
-        private bool _allowOperatorOverloading;
-        private readonly List<IObjectConverter> _objectConverters = new();
-        private Func<Engine, object, ObjectInstance> _wrapObjectHandler;
-        private MemberAccessorDelegate _memberAccessor;
-        private int _maxRecursionDepth = -1;
-        private TimeSpan _regexTimeoutInterval = TimeSpan.FromSeconds(10);
-        private CultureInfo _culture = CultureInfo.CurrentCulture;
-        private TimeZoneInfo _localTimeZone = TimeZoneInfo.Local;
-        private List<Assembly> _lookupAssemblies = new();
-        private Predicate<Exception> _clrExceptionsHandler;
-        private IReferenceResolver _referenceResolver = DefaultReferenceResolver.Instance;
-        private TypeResolver _typeResolver = TypeResolver.Default;
-        private readonly List<Action<Engine>> _configurations = new();
-
-        private readonly List<Type> _extensionMethodClassTypes = new();
-        internal ExtensionMethodCache _extensionMethods = ExtensionMethodCache.Empty;
-        internal Func<Engine, Host> _hostFactory = _ => new Host();
+        internal List<Action<Engine>> _configurations { get; } = new();
 
 
         /// <summary>
         /// <summary>
-        /// Run the script in strict mode.
+        /// Execution constraints for the engine.
         /// </summary>
         /// </summary>
-        public Options Strict(bool strict = true)
-        {
-            _strict = strict;
-            return this;
-        }
+        public ConstraintOptions Constraints { get; } = new();
 
 
         /// <summary>
         /// <summary>
-        /// Selects the handling for script <code>debugger</code> statements.
+        /// CLR interop related options.
         /// </summary>
         /// </summary>
-        /// <remarks>
-        /// The <c>debugger</c> statement can either be ignored (default) trigger debugging at CLR level (e.g. Visual Studio),
-        /// or trigger a break in Jint's DebugHandler.
-        /// </remarks>
-        public Options DebuggerStatementHandling(DebuggerStatementHandling debuggerStatementHandling)
-        {
-            _debuggerStatementHandling = debuggerStatementHandling;
-            return this;
-        }
+        public InteropOptions Interop { get; } = new();
 
 
         /// <summary>
         /// <summary>
-        /// Allow to run the script in debug mode.
+        /// Debugger configuration.
         /// </summary>
         /// </summary>
-        public Options DebugMode(bool debugMode = true)
-        {
-            IsDebugMode = debugMode;
-            return this;
-        }
+        public DebuggerOptions Debugger { get; } = new();
 
 
         /// <summary>
         /// <summary>
-        /// Adds a <see cref="IObjectConverter"/> instance to convert CLR types to <see cref="JsValue"/>
+        /// Host options.
         /// </summary>
         /// </summary>
-        public Options AddObjectConverter<T>() where T : IObjectConverter, new()
-        {
-            return AddObjectConverter(new T());
-        }
+        internal HostOptions Host { get; } = new();
 
 
         /// <summary>
         /// <summary>
-        /// Adds a <see cref="IObjectConverter"/> instance to convert CLR types to <see cref="JsValue"/>
+        /// Whether the code should be always considered to be in strict mode. Can improve performance.
         /// </summary>
         /// </summary>
-        public Options AddObjectConverter(IObjectConverter objectConverter)
-        {
-            _objectConverters.Add(objectConverter);
-            return this;
-        }
+        public bool Strict { get; set; }
+
+        /// <summary>
+        /// The culture the engine runs on, defaults to current culture.
+        /// </summary>
+        public CultureInfo Culture { get; set; } = CultureInfo.CurrentCulture;
+
+        /// <summary>
+        /// The time zone the engine runs on, defaults to local.
+        /// </summary>
+        public TimeZoneInfo TimeZone { get; set; } = TimeZoneInfo.Local;
 
 
-        public Options AddExtensionMethods(params Type[] types)
+        /// <summary>
+        /// Reference resolver allows customizing behavior for reference resolving. This can be useful in cases where
+        /// you want to ignore long chain of property accesses that might throw if anything is null or undefined.
+        /// An example of such is <code>var a = obj.field.subField.value</code>. Custom resolver could accept chain to return
+        /// null/undefined on first occurrence.
+        /// </summary>
+        public IReferenceResolver ReferenceResolver { get; set; } = DefaultReferenceResolver.Instance;
+
+        /// <summary>
+        /// Called by the <see cref="Engine"/> instance that loads this <see cref="Options" />
+        /// once it is loaded.
+        /// </summary>
+        internal void Apply(Engine engine)
         {
         {
-            _extensionMethodClassTypes.AddRange(types);
-            _extensionMethods = ExtensionMethodCache.Build(_extensionMethodClassTypes);
-            return this;
+            foreach (var configuration in _configurations)
+            {
+                configuration?.Invoke(engine);
+            }
+
+            // add missing bits if needed
+            if (Interop.Enabled)
+            {
+                engine.Realm.GlobalObject.SetProperty("System",
+                    new PropertyDescriptor(new NamespaceReference(engine, "System"), PropertyFlag.AllForbidden));
+                engine.Realm.GlobalObject.SetProperty("importNamespace", new PropertyDescriptor(new ClrFunctionInstance(
+                        engine,
+                        "importNamespace",
+                        (thisObj, arguments) =>
+                            new NamespaceReference(engine, TypeConverter.ToString(arguments.At(0)))),
+                    PropertyFlag.AllForbidden));
+            }
+
+            if (Interop.ExtensionMethodTypes.Count > 0)
+            {
+                AttachExtensionMethodsToPrototypes(engine);
+            }
+
+            // ensure defaults
+            engine.ClrTypeConverter ??= new DefaultTypeConverter(engine);
         }
         }
 
 
-        private void AttachExtensionMethodsToPrototypes(Engine engine)
+        private static void AttachExtensionMethodsToPrototypes(Engine engine)
         {
         {
             AttachExtensionMethodsToPrototype(engine, engine.Realm.Intrinsics.Array.PrototypeObject, typeof(Array));
             AttachExtensionMethodsToPrototype(engine, engine.Realm.Intrinsics.Array.PrototypeObject, typeof(Array));
             AttachExtensionMethodsToPrototype(engine, engine.Realm.Intrinsics.Boolean.PrototypeObject, typeof(bool));
             AttachExtensionMethodsToPrototype(engine, engine.Realm.Intrinsics.Boolean.PrototypeObject, typeof(bool));
@@ -108,30 +112,30 @@ namespace Jint
             AttachExtensionMethodsToPrototype(engine, engine.Realm.Intrinsics.String.PrototypeObject, typeof(string));
             AttachExtensionMethodsToPrototype(engine, engine.Realm.Intrinsics.String.PrototypeObject, typeof(string));
         }
         }
 
 
-        private void AttachExtensionMethodsToPrototype(Engine engine, ObjectInstance prototype, Type objectType)
+        private static void AttachExtensionMethodsToPrototype(Engine engine, ObjectInstance prototype, Type objectType)
         {
         {
-            if (!_extensionMethods.TryGetExtensionMethods(objectType, out var methods))
+            if (!engine._extensionMethods.TryGetExtensionMethods(objectType, out var methods))
             {
             {
                 return;
                 return;
             }
             }
 
 
             foreach (var overloads in methods.GroupBy(x => x.Name))
             foreach (var overloads in methods.GroupBy(x => x.Name))
             {
             {
-
-                PropertyDescriptor CreateMethodInstancePropertyDescriptor(ClrFunctionInstance clrFunctionInstance)
+                PropertyDescriptor CreateMethodInstancePropertyDescriptor(ClrFunctionInstance? function)
                 {
                 {
-                    var instance = clrFunctionInstance == null
+                    var instance = function is null
                         ? new MethodInfoFunctionInstance(engine, MethodDescriptor.Build(overloads.ToList()))
                         ? new MethodInfoFunctionInstance(engine, MethodDescriptor.Build(overloads.ToList()))
-                        : new MethodInfoFunctionInstance(engine, MethodDescriptor.Build(overloads.ToList()), clrFunctionInstance);
+                        : new MethodInfoFunctionInstance(engine, MethodDescriptor.Build(overloads.ToList()), function);
 
 
                     return new PropertyDescriptor(instance, PropertyFlag.NonConfigurable);
                     return new PropertyDescriptor(instance, PropertyFlag.NonConfigurable);
                 }
                 }
 
 
                 JsValue key = overloads.Key;
                 JsValue key = overloads.Key;
-                PropertyDescriptor descriptorWithFallback = null;
-                PropertyDescriptor descriptorWithoutFallback = null;
+                PropertyDescriptor? descriptorWithFallback = null;
+                PropertyDescriptor? descriptorWithoutFallback = null;
 
 
-                if (prototype.HasOwnProperty(key) && prototype.GetOwnProperty(key).Value is ClrFunctionInstance clrFunctionInstance)
+                if (prototype.HasOwnProperty(key) &&
+                    prototype.GetOwnProperty(key).Value is ClrFunctionInstance clrFunctionInstance)
                 {
                 {
                     descriptorWithFallback = CreateMethodInstancePropertyDescriptor(clrFunctionInstance);
                     descriptorWithFallback = CreateMethodInstancePropertyDescriptor(clrFunctionInstance);
                     prototype.SetOwnProperty(key, descriptorWithFallback);
                     prototype.SetOwnProperty(key, descriptorWithFallback);
@@ -147,278 +151,125 @@ namespace Jint
                 {
                 {
                     key = char.ToLower(overloads.Key[0]) + overloads.Key.Substring(1);
                     key = char.ToLower(overloads.Key[0]) + overloads.Key.Substring(1);
 
 
-                    if (prototype.HasOwnProperty(key) && prototype.GetOwnProperty(key).Value is ClrFunctionInstance lowerclrFunctionInstance)
+                    if (prototype.HasOwnProperty(key) &&
+                        prototype.GetOwnProperty(key).Value is ClrFunctionInstance lowerclrFunctionInstance)
                     {
                     {
-                        descriptorWithFallback = descriptorWithFallback ?? CreateMethodInstancePropertyDescriptor(lowerclrFunctionInstance);
+                        descriptorWithFallback ??= CreateMethodInstancePropertyDescriptor(lowerclrFunctionInstance);
                         prototype.SetOwnProperty(key, descriptorWithFallback);
                         prototype.SetOwnProperty(key, descriptorWithFallback);
                     }
                     }
                     else
                     else
                     {
                     {
-                        descriptorWithoutFallback = descriptorWithoutFallback ?? CreateMethodInstancePropertyDescriptor(null);
+                        descriptorWithoutFallback ??= CreateMethodInstancePropertyDescriptor(null);
                         prototype.SetOwnProperty(key, descriptorWithoutFallback);
                         prototype.SetOwnProperty(key, descriptorWithoutFallback);
                     }
                     }
                 }
                 }
             }
             }
         }
         }
+    }
 
 
+    public class DebuggerOptions
+    {
         /// <summary>
         /// <summary>
-        /// If no known type could be guessed, objects are normally wrapped as an
-        /// ObjectInstance using class ObjectWrapper. This function can be used to
-        /// register a handler for a customized handling.
+        /// Whether debugger functionality is enabled, defaults to false.
         /// </summary>
         /// </summary>
-        public Options SetWrapObjectHandler(Func<Engine, object, ObjectInstance> wrapObjectHandler)
-        {
-            _wrapObjectHandler = wrapObjectHandler;
-            return this;
-        }
+        public bool Enabled { get; set; }
 
 
         /// <summary>
         /// <summary>
-        /// Sets the type converter to use.
+        /// Configures the statement handling strategy, defaults to Ignore.
         /// </summary>
         /// </summary>
-        public Options SetTypeConverter(Func<Engine, ITypeConverter> typeConverterFactory)
-        {
-            _configurations.Add(engine => engine.ClrTypeConverter = typeConverterFactory(engine));
-            return this;
-        }
+        public DebuggerStatementHandling StatementHandling { get; set; } = DebuggerStatementHandling.Ignore;
+    }
 
 
+    public class InteropOptions
+    {
         /// <summary>
         /// <summary>
-        /// Sets member name comparison strategy when finding CLR objects members.
-        /// By default member's first character casing is ignored and rest of the name is compared with strict equality.
+        /// Whether accessing CLR and it's types and methods is allowed from JS code, defaults to false.
         /// </summary>
         /// </summary>
-        public Options SetTypeResolver(TypeResolver resolver)
-        {
-            _typeResolver = resolver;
-            return this;
-        }
+        public bool Enabled { get; set; }
 
 
         /// <summary>
         /// <summary>
-        /// Registers a delegate that is called when CLR members are invoked. This allows
-        /// to change what values are returned for specific CLR objects, or if any value
-        /// is returned at all.
+        /// Whether writing to CLR objects is allowed (set properties), defaults to true.
         /// </summary>
         /// </summary>
-        /// <param name="accessor">
-        /// The delegate to invoke for each CLR member. If the delegate
-        /// returns <c>null</c>, the standard evaluation is performed.
-        /// </param>
-        public Options SetMemberAccessor(MemberAccessorDelegate accessor)
-        {
-            _memberAccessor = accessor;
-            return this;
-        }
+        public bool AllowWrite { get; set; } = true;
 
 
         /// <summary>
         /// <summary>
-        /// Allows scripts to call CLR types directly like <example>System.IO.File</example>
+        /// Whether operator overloading resolution is allowed, defaults to false.
         /// </summary>
         /// </summary>
-        public Options AllowClr(params Assembly[] assemblies)
-        {
-            _allowClr = true;
-            _lookupAssemblies.AddRange(assemblies);
-            _lookupAssemblies = _lookupAssemblies.Distinct().ToList();
-            return this;
-        }
-
-        public Options AllowClrWrite(bool allow = true)
-        {
-            _allowClrWrite = allow;
-            return this;
-        }
-
-        public Options AllowOperatorOverloading(bool allow = true)
-        {
-            _allowOperatorOverloading = allow;
-            return this;
-        }
+        public bool OperatorOverloadingAllowed { get; set; }
 
 
         /// <summary>
         /// <summary>
-        /// Exceptions thrown from CLR code are converted to JavaScript errors and
-        /// can be used in at try/catch statement. By default these exceptions are bubbled
-        /// to the CLR host and interrupt the script execution.
+        /// Types holding extension methods that should be considered when resolving methods.
         /// </summary>
         /// </summary>
-        public Options CatchClrExceptions()
-        {
-            CatchClrExceptions(_ => true);
-            return this;
-        }
+        public List<Type> ExtensionMethodTypes { get; } = new();
 
 
         /// <summary>
         /// <summary>
-        /// Exceptions that thrown from CLR code are converted to JavaScript errors and
-        /// can be used in at try/catch statement. By default these exceptions are bubbled
-        /// to the CLR host and interrupt the script execution.
+        /// Object converters to try when build-in conversions.
         /// </summary>
         /// </summary>
-        public Options CatchClrExceptions(Predicate<Exception> handler)
-        {
-            _clrExceptionsHandler = handler;
-            return this;
-        }
-
-        public Options Constraint(IConstraint constraint)
-        {
-            if (constraint != null)
-            {
-                _constraints.Add(constraint);
-            }
-            return this;
-        }
-
-        public Options WithoutConstraint(Predicate<IConstraint> predicate)
-        {
-            _constraints.RemoveAll(predicate);
-            return this;
-        }
-
-        public Options RegexTimeoutInterval(TimeSpan regexTimeoutInterval)
-        {
-            _regexTimeoutInterval = regexTimeoutInterval;
-            return this;
-        }
+        public List<IObjectConverter> ObjectConverters { get; } = new();
 
 
         /// <summary>
         /// <summary>
-        /// Sets maximum allowed depth of recursion.
+        /// If no known type could be guessed, objects are by default wrapped as an
+        /// ObjectInstance using class ObjectWrapper. This function can be used to
+        /// change the behavior.
         /// </summary>
         /// </summary>
-        /// <param name="maxRecursionDepth">
-        /// The allowed depth.
-        /// a) In case max depth is zero no recursion is allowed.
-        /// b) In case max depth is equal to n it means that in one scope function can be called no more than n times.
-        /// </param>
-        /// <returns>Options instance for fluent syntax</returns>
-        public Options LimitRecursion(int maxRecursionDepth = 0)
-        {
-            _maxRecursionDepth = maxRecursionDepth;
-            return this;
-        }
+        public WrapObjectDelegate WrapObjectHandler { get; set; } = (engine, target) => new ObjectWrapper(engine, target);
 
 
-        public Options Culture(CultureInfo cultureInfo)
-        {
-            _culture = cultureInfo;
-            return this;
-        }
-
-        public Options LocalTimeZone(TimeZoneInfo timeZoneInfo)
-        {
-            _localTimeZone = timeZoneInfo;
-            return this;
-        }
+        /// <summary>
+        ///
+        /// </summary>
+        public MemberAccessorDelegate MemberAccessor { get; set; } = (engine, target, member) => null;
 
 
-        public Options SetReferencesResolver(IReferenceResolver resolver)
-        {
-            _referenceResolver = resolver;
-            return this;
-        }
+        /// <summary>
+        /// Exceptions that thrown from CLR code are converted to JavaScript errors and
+        /// can be used in at try/catch statement. By default these exceptions are bubbled
+        /// to the CLR host and interrupt the script execution. If handler returns true these exceptions are converted
+        /// to JS errors that can be caught by the script.
+        /// </summary>
+        public ExceptionHandlerDelegate ExceptionHandler { get; set; } = exception => false;
 
 
         /// <summary>
         /// <summary>
-        /// Registers some custom logic to apply on an <see cref="Engine"/> instance when the options
-        /// are loaded.
+        /// Assemblies to allow scripts to call CLR types directly like <example>System.IO.File</example>.
         /// </summary>
         /// </summary>
-        /// <param name="configuration">The action to register.</param>
-        public Options Configure(Action<Engine> configuration)
-        {
-            _configurations.Add(configuration);
-            return this;
-        }
+        public List<Assembly> AllowedAssemblies { get; set; } = new();
 
 
         /// <summary>
         /// <summary>
-        /// Allows to configure how the host is constructed.
+        /// Type and member resolving strategy, which allows filtering allowed members and configuring member
+        /// name matching comparison.
         /// </summary>
         /// </summary>
         /// <remarks>
         /// <remarks>
-        /// Passed Engine instance is still in construction and should not be used during call stage.
+        /// As this object holds caching state same instance should be shared between engines, if possible.
         /// </remarks>
         /// </remarks>
-        public void UseHostFactory<T>(Func<Engine, T> factory) where T : Host
-        {
-            _hostFactory = factory;
-        }
+        public TypeResolver TypeResolver { get; set; } = TypeResolver.Default;
+    }
 
 
+    public class ConstraintOptions
+    {
         /// <summary>
         /// <summary>
-        /// Called by the <see cref="Engine"/> instance that loads this <see cref="Options" />
-        /// once it is loaded.
+        /// Registered constraints.
         /// </summary>
         /// </summary>
-        internal void Apply(Engine engine)
-        {
-            foreach (var configuration in _configurations)
-            {
-                configuration?.Invoke(engine);
-            }
+        public List<IConstraint> Constraints { get; } = new();
 
 
-            // add missing bits if needed
-            if (_allowClr)
-            {
-                engine.Realm.GlobalObject.SetProperty("System", new PropertyDescriptor(new NamespaceReference(engine, "System"), PropertyFlag.AllForbidden));
-                engine.Realm.GlobalObject.SetProperty("importNamespace", new PropertyDescriptor(new ClrFunctionInstance(
-                    engine,
-                    "importNamespace",
-                    func: (thisObj, arguments) => new NamespaceReference(engine, TypeConverter.ToString(arguments.At(0)))), PropertyFlag.AllForbidden));
-            }
-
-            if (_extensionMethodClassTypes.Count > 0)
-            {
-                AttachExtensionMethodsToPrototypes(engine);
-            }
-
-            // ensure defaults
-            engine.ClrTypeConverter ??= new DefaultTypeConverter(engine);
-        }
-
-        internal bool IsStrict => _strict;
-
-        internal DebuggerStatementHandling _DebuggerStatementHandling => _debuggerStatementHandling;
-
-        internal bool IsDebugMode { get; private set; }
-
-        internal bool _IsClrWriteAllowed => _allowClrWrite;
-
-        internal bool _IsOperatorOverloadingAllowed => _allowOperatorOverloading;
-
-        internal Predicate<Exception> _ClrExceptionsHandler => _clrExceptionsHandler;
-
-        internal List<Assembly> _LookupAssemblies => _lookupAssemblies;
-
-        internal List<IObjectConverter> _ObjectConverters => _objectConverters;
-
-        internal List<IConstraint> _Constraints => _constraints;
-
-        internal Func<Engine, object, ObjectInstance> _WrapObjectHandler => _wrapObjectHandler;
-
-        internal MemberAccessorDelegate _MemberAccessor => _memberAccessor;
-        internal TypeResolver _TypeResolver => _typeResolver;
-
-        internal int MaxRecursionDepth => _maxRecursionDepth;
-
-        internal TimeSpan _RegexTimeoutInterval => _regexTimeoutInterval;
-
-        internal CultureInfo _Culture => _culture;
-
-        internal TimeZoneInfo _LocalTimeZone => _localTimeZone;
-
-        internal IReferenceResolver ReferenceResolver => _referenceResolver;
-
-        private sealed class DefaultReferenceResolver : IReferenceResolver
-        {
-            public static readonly DefaultReferenceResolver Instance = new();
-
-            private DefaultReferenceResolver()
-            {
-            }
-
-            public bool TryUnresolvableReference(Engine engine, Reference reference, out JsValue value)
-            {
-                value = JsValue.Undefined;
-                return false;
-            }
+        /// <summary>
+        /// Maximum recursion depth allowed, defaults to -1 (no checks).
+        /// </summary>
+        public int MaxRecursionDepth { get; set; } = -1;
 
 
-            public bool TryPropertyReference(Engine engine, Reference reference, ref JsValue value)
-            {
-                return false;
-            }
+        /// <summary>
+        /// Maximum time a Regex is allowed to run, defaults to 10 seconds.
+        /// </summary>
+        public TimeSpan RegexTimeout { get; set; } = TimeSpan.FromSeconds(10);
 
 
-            public bool TryGetCallable(Engine engine, object callee, out JsValue value)
-            {
-                value = JsValue.Undefined;
-                return false;
-            }
+        /// <summary>
+        /// The maximum size for JavaScript array, defaults to <see cref="uint.MaxValue"/>.
+        /// </summary>
+        public uint MaxArraySize { get; set; } = uint.MaxValue;
+    }
 
 
-            public bool CheckCoercible(JsValue value)
-            {
-                return false;
-            }
-        }
+    /// <summary>
+    /// Host related customization, still work in progress.
+    /// </summary>
+    public class HostOptions
+    {
+        internal Func<Engine, Host> Factory { get; set; } = _ => new Host();
     }
     }
-}
+}

+ 37 - 0
Jint/Runtime/DefaultReferenceResolver.cs

@@ -0,0 +1,37 @@
+using Jint.Native;
+using Jint.Runtime.Interop;
+using Jint.Runtime.References;
+
+namespace Jint.Runtime
+{
+    internal sealed class DefaultReferenceResolver : IReferenceResolver
+    {
+        public static readonly DefaultReferenceResolver Instance = new();
+
+        private DefaultReferenceResolver()
+        {
+        }
+
+        public bool TryUnresolvableReference(Engine engine, Reference reference, out JsValue value)
+        {
+            value = JsValue.Undefined;
+            return false;
+        }
+
+        public bool TryPropertyReference(Engine engine, Reference reference, ref JsValue value)
+        {
+            return false;
+        }
+
+        public bool TryGetCallable(Engine engine, object callee, out JsValue value)
+        {
+            value = JsValue.Undefined;
+            return false;
+        }
+
+        public bool CheckCoercible(JsValue value)
+        {
+            return false;
+        }
+    }
+}

+ 2 - 2
Jint/Runtime/Descriptors/Specialized/ReflectionDescriptor.cs

@@ -19,10 +19,10 @@ namespace Jint.Runtime.Descriptors.Specialized
             _engine = engine;
             _engine = engine;
             _reflectionAccessor = reflectionAccessor;
             _reflectionAccessor = reflectionAccessor;
             _target = target;
             _target = target;
-            Writable = reflectionAccessor.Writable && engine.Options._IsClrWriteAllowed;
+            Writable = reflectionAccessor.Writable && engine.Options.Interop.AllowWrite;
         }
         }
 
 
-       
+
         protected internal override JsValue CustomValue
         protected internal override JsValue CustomValue
         {
         {
             get
             get

+ 1 - 2
Jint/Runtime/ExceptionHelper.cs

@@ -136,8 +136,7 @@ namespace Jint.Runtime
         {
         {
             var meaningfulException = exception.InnerException ?? exception;
             var meaningfulException = exception.InnerException ?? exception;
 
 
-            var handler = engine.Options._ClrExceptionsHandler;
-            if (handler != null && handler(meaningfulException))
+            if (engine.Options.Interop.ExceptionHandler(meaningfulException))
             {
             {
                 ThrowError(engine, meaningfulException.Message);
                 ThrowError(engine, meaningfulException.Message);
             }
             }

+ 124 - 0
Jint/Runtime/Interop/DefaultObjectConverter.cs

@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Jint.Native;
+using Jint.Native.Array;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Interop;
+
+namespace Jint
+{
+    internal static class DefaultObjectConverter
+    {
+        private static Dictionary<Type, Func<Engine, object, JsValue>> _typeMappers = new()
+        {
+            { typeof(bool), (engine, v) => (bool)v ? JsBoolean.True : JsBoolean.False },
+            { typeof(byte), (engine, v) => JsNumber.Create((byte)v) },
+            { typeof(char), (engine, v) => JsString.Create((char)v) },
+            { typeof(DateTime), (engine, v) => engine.Realm.Intrinsics.Date.Construct((DateTime)v) },
+            { typeof(DateTimeOffset), (engine, v) => engine.Realm.Intrinsics.Date.Construct((DateTimeOffset)v) },
+            { typeof(decimal), (engine, v) => (JsValue)(double)(decimal)v },
+            { typeof(double), (engine, v) => (JsValue)(double)v },
+            { typeof(short), (engine, v) => JsNumber.Create((short)v) },
+            { typeof(int), (engine, v) => JsNumber.Create((int)v) },
+            { typeof(long), (engine, v) => (JsValue)(long)v },
+            { typeof(sbyte), (engine, v) => JsNumber.Create((sbyte)v) },
+            { typeof(float), (engine, v) => (JsValue)(float)v },
+            { typeof(string), (engine, v) => JsString.Create((string)v) },
+            { typeof(ushort), (engine, v) => JsNumber.Create((ushort)v) },
+            { typeof(uint), (engine, v) => JsNumber.Create((uint)v) },
+            { typeof(ulong), (engine, v) => JsNumber.Create((ulong)v) },
+            {
+                typeof(System.Text.RegularExpressions.Regex),
+                (engine, v) => engine.Realm.Intrinsics.RegExp.Construct((System.Text.RegularExpressions.Regex)v, "")
+            }
+        };
+
+        public static bool TryConvert(Engine engine, object value, out JsValue result)
+        {
+            var valueType = value.GetType();
+
+            var typeMappers = _typeMappers;
+
+            if (typeMappers.TryGetValue(valueType, out var typeMapper))
+            {
+                result = typeMapper(engine, value);
+            }
+            else
+            {
+                if (value is Array a)
+                {
+                    // racy, we don't care, worst case we'll catch up later
+                    Interlocked.CompareExchange(ref _typeMappers,
+                        new Dictionary<Type, Func<Engine, object, JsValue>>(typeMappers)
+                        {
+                            [valueType] = ConvertArray
+                        }, typeMappers);
+
+                    result = ConvertArray(engine, a);
+                }
+                else
+                {
+                    if (value is Delegate d)
+                    {
+                        result = new DelegateWrapper(engine, d);
+                    }
+                    else
+                    {
+                        var t = value.GetType();
+                        if (t.IsEnum)
+                        {
+                            var ut = Enum.GetUnderlyingType(t);
+
+                            if (ut == typeof(ulong))
+                            {
+                                result = JsNumber.Create(Convert.ToDouble(value));
+                            }
+                            else
+                            {
+                                if (ut == typeof(uint) || ut == typeof(long))
+                                {
+                                    result = JsNumber.Create(Convert.ToInt64(value));
+                                }
+                                else
+                                {
+                                    result = JsNumber.Create(Convert.ToInt32(value));
+                                }
+                            }
+                        }
+                        else
+                        {
+                            result = engine.Options.Interop.WrapObjectHandler.Invoke(engine, value);
+                        }
+
+                        // if no known type could be guessed, use the default of wrapping using using ObjectWrapper.
+                    }
+                }
+            }
+
+            return result is not null;
+        }
+
+        private static JsValue ConvertArray(Engine e, object v)
+        {
+            var array = (Array)v;
+            var arrayLength = (uint)array.Length;
+
+            var jsArray = new ArrayInstance(e, arrayLength)
+            {
+                _prototype = e.Realm.Intrinsics.Array.PrototypeObject
+            };
+
+            for (uint i = 0; i < arrayLength; ++i)
+            {
+                var jsItem = JsValue.FromObject(e, array.GetValue(i));
+                jsArray.WriteArrayValue(i, new PropertyDescriptor(jsItem, PropertyFlag.ConfigurableEnumerableWritable));
+            }
+
+            jsArray.SetOwnProperty(CommonProperties.Length,
+                new PropertyDescriptor(arrayLength, PropertyFlag.OnlyWritable));
+            return jsArray;
+        }
+    }
+}

+ 1 - 1
Jint/Runtime/Interop/DefaultTypeConverter.cs

@@ -213,7 +213,7 @@ namespace Jint.Runtime.Interop
                 return obj;
                 return obj;
             }
             }
 
 
-            if (_engine.Options._IsOperatorOverloadingAllowed)
+            if (_engine.Options.Interop.OperatorOverloadingAllowed)
             {
             {
 #if NETSTANDARD
 #if NETSTANDARD
                 var key = (valueType, type);
                 var key = (valueType, type);

+ 1 - 1
Jint/Runtime/Interop/NamespaceReference.cs

@@ -109,7 +109,7 @@ namespace Jint.Runtime.Interop
 
 
             // search in lookup assemblies
             // search in lookup assemblies
             var comparedPath = path.Replace("+", ".");
             var comparedPath = path.Replace("+", ".");
-            foreach (var assembly in _engine.Options._LookupAssemblies)
+            foreach (var assembly in _engine.Options.Interop.AllowedAssemblies)
             {
             {
                 type = assembly.GetType(path);
                 type = assembly.GetType(path);
                 if (type != null)
                 if (type != null)

+ 7 - 7
Jint/Runtime/Interop/ObjectWrapper.cs

@@ -55,10 +55,10 @@ namespace Jint.Runtime.Interop
                 if (_properties is null || !_properties.ContainsKey(member))
                 if (_properties is null || !_properties.ContainsKey(member))
                 {
                 {
                     // can try utilize fast path
                     // can try utilize fast path
-                    var accessor = _engine.Options._TypeResolver.GetAccessor(_engine, Target.GetType(), member);
+                    var accessor = _engine.Options.Interop.TypeResolver.GetAccessor(_engine, Target.GetType(), member);
 
 
                     // CanPut logic
                     // CanPut logic
-                    if (!accessor.Writable || !_engine.Options._IsClrWriteAllowed)
+                    if (!accessor.Writable || !_engine.Options.Interop.AllowWrite)
                     {
                     {
                         return false;
                         return false;
                     }
                     }
@@ -106,7 +106,7 @@ namespace Jint.Runtime.Interop
             if (property is JsString stringKey)
             if (property is JsString stringKey)
             {
             {
                 var member = stringKey.ToString();
                 var member = stringKey.ToString();
-                var result = Engine.Options._MemberAccessor?.Invoke(Engine, Target, member);
+                var result = Engine.Options.Interop.MemberAccessor?.Invoke(Engine, Target, member);
                 if (result is not null)
                 if (result is not null)
                 {
                 {
                     return result;
                     return result;
@@ -115,7 +115,7 @@ namespace Jint.Runtime.Interop
                 if (_properties is null || !_properties.ContainsKey(member))
                 if (_properties is null || !_properties.ContainsKey(member))
                 {
                 {
                     // can try utilize fast path
                     // can try utilize fast path
-                    var accessor = _engine.Options._TypeResolver.GetAccessor(_engine, Target.GetType(), member);
+                    var accessor = _engine.Options.Interop.TypeResolver.GetAccessor(_engine, Target.GetType(), member);
                     var value = accessor.GetValue(_engine, Target);
                     var value = accessor.GetValue(_engine, Target);
                     if (value is not null)
                     if (value is not null)
                     {
                     {
@@ -226,13 +226,13 @@ namespace Jint.Runtime.Interop
             }
             }
 
 
             var member = property.ToString();
             var member = property.ToString();
-            var result = Engine.Options._MemberAccessor?.Invoke(Engine, Target, member);
+            var result = Engine.Options.Interop.MemberAccessor?.Invoke(Engine, Target, member);
             if (result is not null)
             if (result is not null)
             {
             {
                 return new PropertyDescriptor(result, PropertyFlag.OnlyEnumerable);
                 return new PropertyDescriptor(result, PropertyFlag.OnlyEnumerable);
             }
             }
 
 
-            var accessor = _engine.Options._TypeResolver.GetAccessor(_engine, Target.GetType(), member);
+            var accessor = _engine.Options.Interop.TypeResolver.GetAccessor(_engine, Target.GetType(), member);
             var descriptor = accessor.CreatePropertyDescriptor(_engine, Target);
             var descriptor = accessor.CreatePropertyDescriptor(_engine, Target);
             SetProperty(member, descriptor);
             SetProperty(member, descriptor);
             return descriptor;
             return descriptor;
@@ -252,7 +252,7 @@ namespace Jint.Runtime.Interop
                     _ => null
                     _ => null
                 };
                 };
             }
             }
-            return engine.Options._TypeResolver.GetAccessor(engine, target.GetType(), member.Name, Factory).CreatePropertyDescriptor(engine, target);
+            return engine.Options.Interop.TypeResolver.GetAccessor(engine, target.GetType(), member.Name, Factory).CreatePropertyDescriptor(engine, target);
         }
         }
 
 
         public override bool Equals(JsValue obj)
         public override bool Equals(JsValue obj)

+ 5 - 0
Jint/Runtime/Interop/Reflection/ExtensionMethodCache.cs

@@ -27,6 +27,11 @@ namespace Jint.Runtime.Interop.Reflection
 
 
         internal static ExtensionMethodCache Build(List<Type> extensionMethodContainerTypes)
         internal static ExtensionMethodCache Build(List<Type> extensionMethodContainerTypes)
         {
         {
+            if (extensionMethodContainerTypes.Count == 0)
+            {
+                return Empty;
+            }
+
             Type GetTypeDefinition(Type type)
             Type GetTypeDefinition(Type type)
             {
             {
                 return type.IsConstructedGenericType && type.GenericTypeArguments.Any(x => x.IsGenericParameter) ?
                 return type.IsConstructedGenericType && type.GenericTypeArguments.Any(x => x.IsGenericParameter) ?

+ 1 - 1
Jint/Runtime/Interop/Reflection/IndexerAccessor.cs

@@ -60,7 +60,7 @@ namespace Jint.Runtime.Interop.Reflection
                 return null;
                 return null;
             }
             }
 
 
-            var filter = engine.Options._TypeResolver.MemberFilter;
+            var filter = engine.Options.Interop.TypeResolver.MemberFilter;
 
 
             // default indexer wins
             // default indexer wins
             if (typeof(IList).IsAssignableFrom(targetType) && filter(_iListIndexer))
             if (typeof(IList).IsAssignableFrom(targetType) && filter(_iListIndexer))

+ 1 - 1
Jint/Runtime/Interop/TypeReference.cs

@@ -143,7 +143,7 @@ namespace Jint.Runtime.Interop
 
 
         private ReflectionAccessor ResolveMemberAccessor(Type type, string name)
         private ReflectionAccessor ResolveMemberAccessor(Type type, string name)
         {
         {
-            var typeResolver = _engine.Options._TypeResolver;
+            var typeResolver = _engine.Options.Interop.TypeResolver;
 
 
             if (type.IsEnum)
             if (type.IsEnum)
             {
             {

+ 1 - 1
Jint/Runtime/Interop/TypeResolver.cs

@@ -142,7 +142,7 @@ namespace Jint.Runtime.Interop
                 }
                 }
             }
             }
 
 
-            if (engine.Options._extensionMethods.TryGetExtensionMethods(type, out var extensionMethods))
+            if (engine._extensionMethods.TryGetExtensionMethods(type, out var extensionMethods))
             {
             {
                 var matches = new List<MethodInfo>();
                 var matches = new List<MethodInfo>();
                 foreach (var method in extensionMethods)
                 foreach (var method in extensionMethods)

+ 1 - 1
Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs

@@ -45,7 +45,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             var lval = _engine.GetValue(lref, false);
             var lval = _engine.GetValue(lref, false);
             var handledByOverload = false;
             var handledByOverload = false;
 
 
-            if (_engine.Options._IsOperatorOverloadingAllowed)
+            if (_engine.Options.Interop.OperatorOverloadingAllowed)
             {
             {
                 string operatorClrName = null;
                 string operatorClrName = null;
                 switch (_operator)
                 switch (_operator)

+ 10 - 10
Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs

@@ -270,7 +270,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var left = _left.GetValue();
                 var left = _left.GetValue();
                 var right = _right.GetValue();
                 var right = _right.GetValue();
 
 
-                if (_engine.Options._IsOperatorOverloadingAllowed
+                if (_engine.Options.Interop.OperatorOverloadingAllowed
                     && TryOperatorOverloading(left, right, "op_LessThan", out var opResult))
                     && TryOperatorOverloading(left, right, "op_LessThan", out var opResult))
                 {
                 {
                     return opResult;
                     return opResult;
@@ -294,7 +294,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var left = _left.GetValue();
                 var left = _left.GetValue();
                 var right = _right.GetValue();
                 var right = _right.GetValue();
 
 
-                if (_engine.Options._IsOperatorOverloadingAllowed
+                if (_engine.Options.Interop.OperatorOverloadingAllowed
                     && TryOperatorOverloading(left, right, "op_GreaterThan", out var opResult))
                     && TryOperatorOverloading(left, right, "op_GreaterThan", out var opResult))
                 {
                 {
                     return opResult;
                     return opResult;
@@ -319,7 +319,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var left = _left.GetValue();
                 var left = _left.GetValue();
                 var right = _right.GetValue();
                 var right = _right.GetValue();
 
 
-                if (_engine.Options._IsOperatorOverloadingAllowed
+                if (_engine.Options.Interop.OperatorOverloadingAllowed
                     && TryOperatorOverloading(left, right, "op_Addition", out var opResult))
                     && TryOperatorOverloading(left, right, "op_Addition", out var opResult))
                 {
                 {
                     return opResult;
                     return opResult;
@@ -348,7 +348,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var left = _left.GetValue();
                 var left = _left.GetValue();
                 var right = _right.GetValue();
                 var right = _right.GetValue();
 
 
-                if (_engine.Options._IsOperatorOverloadingAllowed
+                if (_engine.Options.Interop.OperatorOverloadingAllowed
                     && TryOperatorOverloading(left, right, "op_Subtraction", out var opResult))
                     && TryOperatorOverloading(left, right, "op_Subtraction", out var opResult))
                 {
                 {
                     return opResult;
                     return opResult;
@@ -371,7 +371,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var left = _left.GetValue();
                 var left = _left.GetValue();
                 var right = _right.GetValue();
                 var right = _right.GetValue();
 
 
-                if (_engine.Options._IsOperatorOverloadingAllowed
+                if (_engine.Options.Interop.OperatorOverloadingAllowed
                     && TryOperatorOverloading(left, right, "op_Multiply", out var opResult))
                     && TryOperatorOverloading(left, right, "op_Multiply", out var opResult))
                 {
                 {
                     return opResult;
                     return opResult;
@@ -402,7 +402,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var left = _left.GetValue();
                 var left = _left.GetValue();
                 var right = _right.GetValue();
                 var right = _right.GetValue();
 
 
-                if (_engine.Options._IsOperatorOverloadingAllowed
+                if (_engine.Options.Interop.OperatorOverloadingAllowed
                     && TryOperatorOverloading(left, right, "op_Division", out var opResult))
                     && TryOperatorOverloading(left, right, "op_Division", out var opResult))
                 {
                 {
                     return opResult;
                     return opResult;
@@ -426,7 +426,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var left = _left.GetValue();
                 var left = _left.GetValue();
                 var right = _right.GetValue();
                 var right = _right.GetValue();
 
 
-                if (_engine.Options._IsOperatorOverloadingAllowed
+                if (_engine.Options.Interop.OperatorOverloadingAllowed
                     && TryOperatorOverloading(left, right, _invert ? "op_Inequality" : "op_Equality", out var opResult))
                     && TryOperatorOverloading(left, right, _invert ? "op_Inequality" : "op_Equality", out var opResult))
                 {
                 {
                     return opResult;
                     return opResult;
@@ -452,7 +452,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var leftValue = _left.GetValue();
                 var leftValue = _left.GetValue();
                 var rightValue = _right.GetValue();
                 var rightValue = _right.GetValue();
 
 
-                if (_engine.Options._IsOperatorOverloadingAllowed
+                if (_engine.Options.Interop.OperatorOverloadingAllowed
                     && TryOperatorOverloading(leftValue, rightValue, _leftFirst ? "op_GreaterThanOrEqual" : "op_LessThanOrEqual", out var opResult))
                     && TryOperatorOverloading(leftValue, rightValue, _leftFirst ? "op_GreaterThanOrEqual" : "op_LessThanOrEqual", out var opResult))
                 {
                 {
                     return opResult;
                     return opResult;
@@ -529,7 +529,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var left = _left.GetValue();
                 var left = _left.GetValue();
                 var right = _right.GetValue();
                 var right = _right.GetValue();
 
 
-                if (_engine.Options._IsOperatorOverloadingAllowed
+                if (_engine.Options.Interop.OperatorOverloadingAllowed
                     && TryOperatorOverloading(left, right, "op_Modulus", out var opResult))
                     && TryOperatorOverloading(left, right, "op_Modulus", out var opResult))
                 {
                 {
                     return opResult;
                     return opResult;
@@ -592,7 +592,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var left = _left.GetValue();
                 var left = _left.GetValue();
                 var right = _right.GetValue();
                 var right = _right.GetValue();
 
 
-                if (_engine.Options._IsOperatorOverloadingAllowed
+                if (_engine.Options.Interop.OperatorOverloadingAllowed
                     && TryOperatorOverloading(left, right, OperatorClrName, out var opResult))
                     && TryOperatorOverloading(left, right, OperatorClrName, out var opResult))
                 {
                 {
                     return opResult;
                     return opResult;

+ 4 - 4
Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs

@@ -58,7 +58,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 case UnaryOperator.Plus:
                 case UnaryOperator.Plus:
                 {
                 {
                     var v = _argument.GetValue();
                     var v = _argument.GetValue();
-                    if (_engine.Options._IsOperatorOverloadingAllowed &&
+                    if (_engine.Options.Interop.OperatorOverloadingAllowed &&
                         TryOperatorOverloading(_engine, v, "op_UnaryPlus", out var result))
                         TryOperatorOverloading(_engine, v, "op_UnaryPlus", out var result))
                     {
                     {
                         return result;
                         return result;
@@ -71,7 +71,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 case UnaryOperator.Minus:
                 case UnaryOperator.Minus:
                 {
                 {
                     var v = _argument.GetValue();
                     var v = _argument.GetValue();
-                    if (_engine.Options._IsOperatorOverloadingAllowed &&
+                    if (_engine.Options.Interop.OperatorOverloadingAllowed &&
                         TryOperatorOverloading(_engine, v, "op_UnaryNegation", out var result))
                         TryOperatorOverloading(_engine, v, "op_UnaryNegation", out var result))
                     {
                     {
                         return result;
                         return result;
@@ -82,7 +82,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 case UnaryOperator.BitwiseNot:
                 case UnaryOperator.BitwiseNot:
                 {
                 {
                     var v = _argument.GetValue();
                     var v = _argument.GetValue();
-                    if (_engine.Options._IsOperatorOverloadingAllowed &&
+                    if (_engine.Options.Interop.OperatorOverloadingAllowed &&
                         TryOperatorOverloading(_engine, v, "op_OnesComplement", out var result))
                         TryOperatorOverloading(_engine, v, "op_OnesComplement", out var result))
                     {
                     {
                         return result;
                         return result;
@@ -93,7 +93,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 case UnaryOperator.LogicalNot:
                 case UnaryOperator.LogicalNot:
                 {
                 {
                     var v = _argument.GetValue();
                     var v = _argument.GetValue();
-                    if (_engine.Options._IsOperatorOverloadingAllowed &&
+                    if (_engine.Options.Interop.OperatorOverloadingAllowed &&
                         TryOperatorOverloading(_engine, v, "op_LogicalNot", out var result))
                         TryOperatorOverloading(_engine, v, "op_LogicalNot", out var result))
                     {
                     {
                         return result;
                         return result;

+ 2 - 2
Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs

@@ -66,7 +66,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             JsValue newValue = null;
             JsValue newValue = null;
 
 
             var operatorOverloaded = false;
             var operatorOverloaded = false;
-            if (_engine.Options._IsOperatorOverloadingAllowed)
+            if (_engine.Options.Interop.OperatorOverloadingAllowed)
             {
             {
                 if (JintUnaryExpression.TryOperatorOverloading(_engine, _argument.GetValue(), _change > 0 ? "op_Increment" : "op_Decrement", out var result))
                 if (JintUnaryExpression.TryOperatorOverloading(_engine, _argument.GetValue(), _change > 0 ? "op_Increment" : "op_Decrement", out var result))
                 {
                 {
@@ -113,7 +113,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 JsValue newValue = null;
                 JsValue newValue = null;
 
 
                 var operatorOverloaded = false;
                 var operatorOverloaded = false;
-                if (_engine.Options._IsOperatorOverloadingAllowed)
+                if (_engine.Options.Interop.OperatorOverloadingAllowed)
                 {
                 {
                     if (JintUnaryExpression.TryOperatorOverloading(_engine, _argument.GetValue(), _change > 0 ? "op_Increment" : "op_Decrement", out var result))
                     if (JintUnaryExpression.TryOperatorOverloading(_engine, _argument.GetValue(), _change > 0 ? "op_Increment" : "op_Decrement", out var result))
                     {
                     {

+ 1 - 1
Jint/Runtime/Interpreter/Statements/JintDebuggerStatement.cs

@@ -11,7 +11,7 @@ namespace Jint.Runtime.Interpreter.Statements
 
 
         protected override Completion ExecuteInternal()
         protected override Completion ExecuteInternal()
         {
         {
-            switch (_engine.Options._DebuggerStatementHandling)
+            switch (_engine.Options.Debugger.StatementHandling)
             {
             {
                 case DebuggerStatementHandling.Clr:
                 case DebuggerStatementHandling.Clr:
                     if (!System.Diagnostics.Debugger.IsAttached)
                     if (!System.Diagnostics.Debugger.IsAttached)

+ 1 - 1
Jint/Runtime/TypeConverter.cs

@@ -791,7 +791,7 @@ namespace Jint.Runtime
             Node sourceNode,
             Node sourceNode,
             string referenceName)
             string referenceName)
         {
         {
-            if (!engine.Options.ReferenceResolver.CheckCoercible(o))
+            if (!engine._referenceResolver.CheckCoercible(o))
             {
             {
                 ThrowMemberNullOrUndefinedError(engine, o, sourceNode, referenceName);
                 ThrowMemberNullOrUndefinedError(engine, o, sourceNode, referenceName);
             }
             }