diff --git a/Jint.Tests.PublicInterface/InteropTests.Dynamic.cs b/Jint.Tests.PublicInterface/InteropTests.Dynamic.cs index 10e7cb573e..d99af725f8 100644 --- a/Jint.Tests.PublicInterface/InteropTests.Dynamic.cs +++ b/Jint.Tests.PublicInterface/InteropTests.Dynamic.cs @@ -2,151 +2,150 @@ using Jint.Native; using Jint.Native.Symbol; -namespace Jint.Tests.PublicInterface +namespace Jint.Tests.PublicInterface; + +public partial class InteropTests { - public partial class InteropTests + [Fact] + public void CanAccessExpandoObject() { - [Fact] - public void CanAccessExpandoObject() - { - var engine = new Engine(); - dynamic expando = new ExpandoObject(); - expando.Name = "test"; - engine.SetValue("expando", expando); - Assert.Equal("test", engine.Evaluate("expando.Name").ToString()); - } - - [Fact] - public void DebugView() - { - // allows displaying different local variables under debugger - - var engine = new Engine(); - var boolNet = true; - var boolJint = (JsBoolean) boolNet; - var doubleNet = 12.34; - var doubleJint = (JsNumber) doubleNet; - var integerNet = 42; - var integerJint = (JsNumber) integerNet; - var stringNet = "ABC"; - var stringJint = (JsString) stringNet; - var arrayNet = new[] { 1, 2, 3 }; - var arrayListNet = new List { 1, 2, 3 }; - var arrayJint = new JsArray(engine, arrayNet.Select(x => (JsNumber) x).ToArray()); - - var objectNet = new Person { Name = "name", Age = 12 }; - var objectJint = new JsObject(engine); - objectJint["name"] = "name"; - objectJint["age"] = 12; - objectJint[GlobalSymbolRegistry.ToStringTag] = "Object"; - - var dictionaryNet = new Dictionary(); - dictionaryNet["name"] = "name"; - dictionaryNet["age"] = 12; - dictionaryNet[GlobalSymbolRegistry.ToStringTag] = "Object"; - } + var engine = new Engine(); + dynamic expando = new ExpandoObject(); + expando.Name = "test"; + engine.SetValue("expando", expando); + Assert.Equal("test", engine.Evaluate("expando.Name").ToString()); + } - [Fact] - public void CanAccessMemberNamedItemThroughExpando() - { - var parent = (IDictionary) new ExpandoObject(); - var child = (IDictionary) new ExpandoObject(); - var values = (IDictionary) new ExpandoObject(); + [Fact] + public void DebugView() + { + // allows displaying different local variables under debugger + + var engine = new Engine(); + var boolNet = true; + var boolJint = (JsBoolean) boolNet; + var doubleNet = 12.34; + var doubleJint = (JsNumber) doubleNet; + var integerNet = 42; + var integerJint = (JsNumber) integerNet; + var stringNet = "ABC"; + var stringJint = (JsString) stringNet; + var arrayNet = new[] { 1, 2, 3 }; + var arrayListNet = new List { 1, 2, 3 }; + var arrayJint = new JsArray(engine, arrayNet.Select(x => (JsNumber) x).ToArray()); + + var objectNet = new Person { Name = "name", Age = 12 }; + var objectJint = new JsObject(engine); + objectJint["name"] = "name"; + objectJint["age"] = 12; + objectJint[GlobalSymbolRegistry.ToStringTag] = "Object"; + + var dictionaryNet = new Dictionary(); + dictionaryNet["name"] = "name"; + dictionaryNet["age"] = 12; + dictionaryNet[GlobalSymbolRegistry.ToStringTag] = "Object"; + } - parent["child"] = child; - child["item"] = values; - values["title"] = "abc"; + [Fact] + public void CanAccessMemberNamedItemThroughExpando() + { + var parent = (IDictionary) new ExpandoObject(); + var child = (IDictionary) new ExpandoObject(); + var values = (IDictionary) new ExpandoObject(); - _engine.SetValue("parent", parent); - Assert.Equal("abc", _engine.Evaluate("parent.child.item.title")); - } + parent["child"] = child; + child["item"] = values; + values["title"] = "abc"; - [Fact] - public void ShouldForOfOnExpandoObject() - { - dynamic o = new ExpandoObject(); - o.a = 1; - o.b = 2; + _engine.SetValue("parent", parent); + Assert.Equal("abc", _engine.Evaluate("parent.child.item.title")); + } - _engine.SetValue("dynamic", o); + [Fact] + public void ShouldForOfOnExpandoObject() + { + dynamic o = new ExpandoObject(); + o.a = 1; + o.b = 2; - var result = _engine.Evaluate("var l = ''; for (var x of dynamic) l += x; return l;").AsString(); + _engine.SetValue("dynamic", o); - Assert.Equal("a,1b,2", result); - } + var result = _engine.Evaluate("var l = ''; for (var x of dynamic) l += x; return l;").AsString(); - [Fact] - public void ShouldConvertObjectInstanceToExpando() - { - _engine.Evaluate("var o = {a: 1, b: 'foo'}"); - var result = _engine.GetValue("o"); + Assert.Equal("a,1b,2", result); + } - dynamic value = result.ToObject(); + [Fact] + public void ShouldConvertObjectInstanceToExpando() + { + _engine.Evaluate("var o = {a: 1, b: 'foo'}"); + var result = _engine.GetValue("o"); - Assert.Equal(1, value.a); - Assert.Equal("foo", value.b); + dynamic value = result.ToObject(); - var dic = (IDictionary) result.ToObject(); + Assert.Equal(1, value.a); + Assert.Equal("foo", value.b); - Assert.Equal(1d, dic["a"]); - Assert.Equal("foo", dic["b"]); - } + var dic = (IDictionary) result.ToObject(); - [Fact] - public void CanAccessDynamicObject() - { - var test = new DynamicClass(); - var engine = new Engine(); - - engine.SetValue("test", test); + Assert.Equal(1d, dic["a"]); + Assert.Equal("foo", dic["b"]); + } - Assert.Equal("a", engine.Evaluate("test.a").AsString()); - Assert.Equal("b", engine.Evaluate("test.b").AsString()); + [Fact] + public void CanAccessDynamicObject() + { + var test = new DynamicClass(); + var engine = new Engine(); - engine.Evaluate("test.a = 5; test.b = 10; test.Name = 'Jint'"); + engine.SetValue("test", test); - Assert.Equal(5, engine.Evaluate("test.a").AsNumber()); - Assert.Equal(10, engine.Evaluate("test.b").AsNumber()); + Assert.Equal("a", engine.Evaluate("test.a").AsString()); + Assert.Equal("b", engine.Evaluate("test.b").AsString()); - Assert.Equal("Jint", engine.Evaluate("test.Name").AsString()); - Assert.True(engine.Evaluate("test.ContainsKey('a')").AsBoolean()); - Assert.True(engine.Evaluate("test.ContainsKey('b')").AsBoolean()); - Assert.False(engine.Evaluate("test.ContainsKey('c')").AsBoolean()); - } + engine.Evaluate("test.a = 5; test.b = 10; test.Name = 'Jint'"); - private class DynamicClass : DynamicObject - { - private readonly Dictionary _properties = new Dictionary(); + Assert.Equal(5, engine.Evaluate("test.a").AsNumber()); + Assert.Equal(10, engine.Evaluate("test.b").AsNumber()); - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - result = binder.Name; - if (_properties.TryGetValue(binder.Name, out var value)) - { - result = value; - } + Assert.Equal("Jint", engine.Evaluate("test.Name").AsString()); + Assert.True(engine.Evaluate("test.ContainsKey('a')").AsBoolean()); + Assert.True(engine.Evaluate("test.ContainsKey('b')").AsBoolean()); + Assert.False(engine.Evaluate("test.ContainsKey('c')").AsBoolean()); + } - return true; - } + private class DynamicClass : DynamicObject + { + private readonly Dictionary _properties = new Dictionary(); - public override bool TrySetMember(SetMemberBinder binder, object value) + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + result = binder.Name; + if (_properties.TryGetValue(binder.Name, out var value)) { - _properties[binder.Name] = value; - return true; + result = value; } - public string Name { get; set; } + return true; + } - public bool ContainsKey(string key) - { - return _properties.ContainsKey(key); - } + public override bool TrySetMember(SetMemberBinder binder, object value) + { + _properties[binder.Name] = value; + return true; } - private class Person + public string Name { get; set; } + + public bool ContainsKey(string key) { - public string Name { get; set; } - public int Age { get; set; } + return _properties.ContainsKey(key); } } -} + + private class Person + { + public string Name { get; set; } + public int Age { get; set; } + } +} \ No newline at end of file diff --git a/Jint.Tests.PublicInterface/InteropTests.NewtonsoftJson.cs b/Jint.Tests.PublicInterface/InteropTests.NewtonsoftJson.cs index fbfe53e429..b2a5179d5e 100644 --- a/Jint.Tests.PublicInterface/InteropTests.NewtonsoftJson.cs +++ b/Jint.Tests.PublicInterface/InteropTests.NewtonsoftJson.cs @@ -2,53 +2,53 @@ using Jint.Runtime; using Newtonsoft.Json.Linq; -namespace Jint.Tests.PublicInterface +namespace Jint.Tests.PublicInterface; + +public partial class InteropTests { - public partial class InteropTests + [Fact] + public void AccessingJObjectShouldWork() { - [Fact] - public void AccessingJObjectShouldWork() - { - var o = new JObject - { - new JProperty("name", "test-name") - }; - _engine.SetValue("o", o); - Assert.True(_engine.Evaluate("return o.name == 'test-name'").AsBoolean()); - } - - [Fact] - public void AccessingJArrayViaIntegerIndexShouldWork() - { - var o = new JArray("item1", "item2"); - _engine.SetValue("o", o); - Assert.True(_engine.Evaluate("return o[0] == 'item1'").AsBoolean()); - Assert.True(_engine.Evaluate("return o[1] == 'item2'").AsBoolean()); - } - - [Fact] - public void DictionaryLikeShouldCheckIndexerAndFallBackToProperty() + var o = new JObject { - const string json = @"{ ""Type"": ""Cat"" }"; - var jObjectWithTypeProperty = JObject.Parse(json); + new JProperty("name", "test-name") + }; + _engine.SetValue("o", o); + Assert.True(_engine.Evaluate("return o.name == 'test-name'").AsBoolean()); + } + + [Fact] + public void AccessingJArrayViaIntegerIndexShouldWork() + { + var o = new JArray("item1", "item2"); + _engine.SetValue("o", o); + Assert.True(_engine.Evaluate("return o[0] == 'item1'").AsBoolean()); + Assert.True(_engine.Evaluate("return o[1] == 'item2'").AsBoolean()); + } - _engine.SetValue("o", jObjectWithTypeProperty); + [Fact] + public void DictionaryLikeShouldCheckIndexerAndFallBackToProperty() + { + const string json = @"{ ""Type"": ""Cat"" }"; + var jObjectWithTypeProperty = JObject.Parse(json); - var typeResult = _engine.Evaluate("o.Type"); + _engine.SetValue("o", jObjectWithTypeProperty); - // JToken requires conversion - Assert.Equal("Cat", TypeConverter.ToString(typeResult)); + var typeResult = _engine.Evaluate("o.Type"); - // weak equality does conversions from native types - Assert.True(_engine.Evaluate("o.Type == 'Cat'").AsBoolean()); - } + // JToken requires conversion + Assert.Equal("Cat", TypeConverter.ToString(typeResult)); - [Fact] - public void ShouldBeAbleToIndexJObjectWithStrings() - { - var engine = new Engine(); + // weak equality does conversions from native types + Assert.True(_engine.Evaluate("o.Type == 'Cat'").AsBoolean()); + } + + [Fact] + public void ShouldBeAbleToIndexJObjectWithStrings() + { + var engine = new Engine(); - const string json = @" + const string json = @" { 'Properties': { 'expirationDate': { @@ -57,103 +57,102 @@ public void ShouldBeAbleToIndexJObjectWithStrings() } }"; - var obj = JObject.Parse(json); - engine.SetValue("o", obj); - var value = engine.Evaluate("o.Properties.expirationDate.Value"); - var dateInstance = Assert.IsAssignableFrom(value); - Assert.Equal(DateTime.Parse("2021-10-09T00:00:00Z").ToUniversalTime(), dateInstance.ToDateTime()); - } + var obj = JObject.Parse(json); + engine.SetValue("o", obj); + var value = engine.Evaluate("o.Properties.expirationDate.Value"); + var dateInstance = Assert.IsAssignableFrom(value); + Assert.Equal(DateTime.Parse("2021-10-09T00:00:00Z").ToUniversalTime(), dateInstance.ToDateTime()); + } - // https://github.com/OrchardCMS/OrchardCore/issues/10648 - [Fact] - public void EngineShouldStringifyAnJObjectListWithValuesCorrectly() + // https://github.com/OrchardCMS/OrchardCore/issues/10648 + [Fact] + public void EngineShouldStringifyAnJObjectListWithValuesCorrectly() + { + var engine = new Engine(); + var queryResults = new List { - var engine = new Engine(); - var queryResults = new List - { - new { Text = "Text1", Value = 1 }, - new { Text = "Text2", Value = 2 } - }; + new { Text = "Text1", Value = 1 }, + new { Text = "Text2", Value = 2 } + }; - engine.SetValue("testSubject", queryResults.Select(x => JObject.FromObject(x))); - var fromEngine = engine.Evaluate("return JSON.stringify(testSubject);"); - var result = fromEngine.ToString(); + engine.SetValue("testSubject", queryResults.Select(x => JObject.FromObject(x))); + var fromEngine = engine.Evaluate("return JSON.stringify(testSubject);"); + var result = fromEngine.ToString(); - // currently we do not materialize LINQ enumerables - // Assert.Equal("[{\"Text\":\"Text1\",\"Value\":1},{\"Text\":\"Text2\",\"Value\":2}]", result); + // currently we do not materialize LINQ enumerables + // Assert.Equal("[{\"Text\":\"Text1\",\"Value\":1},{\"Text\":\"Text2\",\"Value\":2}]", result); - Assert.Equal("{\"Current\":null}", result); - } + Assert.Equal("{\"Current\":null}", result); + } - [Fact] - public void EngineShouldStringifyJObjectFromObjectListWithValuesCorrectly() - { - var engine = new Engine(); + [Fact] + public void EngineShouldStringifyJObjectFromObjectListWithValuesCorrectly() + { + var engine = new Engine(); - var source = new dynamic[] - { - new { Text = "Text1", Value = 1 }, - new { Text = "Text2", Value = 2, Null = (object) null, Date = new DateTime(2015, 6, 25, 0, 0, 0, DateTimeKind.Utc) } - }; + var source = new dynamic[] + { + new { Text = "Text1", Value = 1 }, + new { Text = "Text2", Value = 2, Null = (object) null, Date = new DateTime(2015, 6, 25, 0, 0, 0, DateTimeKind.Utc) } + }; - engine.SetValue("testSubject", source.Select(x => JObject.FromObject(x)).ToList()); - var fromEngine = engine.Evaluate("return JSON.stringify(testSubject);"); - var result = fromEngine.ToString(); + engine.SetValue("testSubject", source.Select(x => JObject.FromObject(x)).ToList()); + var fromEngine = engine.Evaluate("return JSON.stringify(testSubject);"); + var result = fromEngine.ToString(); - Assert.Equal("[{\"Text\":\"Text1\",\"Value\":1},{\"Text\":\"Text2\",\"Value\":2,\"Null\":null,\"Date\":\"2015-06-25T00:00:00.000Z\"}]", result); - } + Assert.Equal("[{\"Text\":\"Text1\",\"Value\":1},{\"Text\":\"Text2\",\"Value\":2,\"Null\":null,\"Date\":\"2015-06-25T00:00:00.000Z\"}]", result); + } - [Fact] - public void DecimalsShouldBeHandledFromJObjects() - { - var test = JObject.FromObject(new - { - DecimalValue = 123.456m - }); - _engine.SetValue("test", test); - var fromInterop = _engine.Evaluate("test.DecimalValue"); - var number = Assert.IsType(fromInterop); - Assert.Equal(123.456d, number.AsNumber()); - } - - [Fact] - public void ShouldBeAbleToChangePropertyWithNameValue() + [Fact] + public void DecimalsShouldBeHandledFromJObjects() + { + var test = JObject.FromObject(new { - var engine = new Engine(); + DecimalValue = 123.456m + }); + _engine.SetValue("test", test); + var fromInterop = _engine.Evaluate("test.DecimalValue"); + var number = Assert.IsType(fromInterop); + Assert.Equal(123.456d, number.AsNumber()); + } - var input = Newtonsoft.Json.JsonConvert.DeserializeObject(@"{ ""value"": ""ORIGINAL"" }"); - var result = engine - .SetValue("input", input) - .Evaluate("input.value = \"CHANGED\"; input.value") - .AsString(); + [Fact] + public void ShouldBeAbleToChangePropertyWithNameValue() + { + var engine = new Engine(); - Assert.Equal("CHANGED", result); - } + var input = Newtonsoft.Json.JsonConvert.DeserializeObject(@"{ ""value"": ""ORIGINAL"" }"); + var result = engine + .SetValue("input", input) + .Evaluate("input.value = \"CHANGED\"; input.value") + .AsString(); - [Fact] - public void ArraysShouldPassThroughCorrectly() - { - var engine = new Engine(); + Assert.Equal("CHANGED", result); + } - const string Json = """ - { - 'entries': [ - { 'id': 1, 'name': 'One' }, - { 'id': 2, 'name': 'Two' }, - { 'id': 3, 'name': 'Three' } - ] - } - """; - - var obj = JObject.Parse(Json); - engine.SetValue("o", obj); - - var names = engine.Evaluate("o.entries.map(e => e.name)").AsArray(); - - Assert.Equal((uint) 3, names.Length); - Assert.Equal("One", names[0]); - Assert.Equal("Two", names[1]); - Assert.Equal("Three", names[2]); - } + [Fact] + public void ArraysShouldPassThroughCorrectly() + { + var engine = new Engine(); + + const string Json = """ + { + 'entries': [ + { 'id': 1, 'name': 'One' }, + { 'id': 2, 'name': 'Two' }, + { 'id': 3, 'name': 'Three' } + ] + } + """; + + var obj = JObject.Parse(Json); + engine.SetValue("o", obj); + + var names = engine.Evaluate("o.entries.map(e => e.name)").AsArray(); + + Assert.Equal((uint) 3, names.Length); + Assert.Equal("One", names[0]); + Assert.Equal("Two", names[1]); + Assert.Equal("Three", names[2]); } -} +} \ No newline at end of file diff --git a/Jint.Tests.PublicInterface/InteropTests.cs b/Jint.Tests.PublicInterface/InteropTests.cs index 602cc2f14a..4e4ba4b600 100644 --- a/Jint.Tests.PublicInterface/InteropTests.cs +++ b/Jint.Tests.PublicInterface/InteropTests.cs @@ -1,24 +1,23 @@ using System.Reflection; -namespace Jint.Tests.PublicInterface +namespace Jint.Tests.PublicInterface; + +public partial class InteropTests : IDisposable { - public partial class InteropTests : IDisposable - { - private readonly Engine _engine; + private readonly Engine _engine; - public InteropTests() - { - _engine = new Engine(cfg => cfg.AllowClr( - typeof(Console).GetTypeInfo().Assembly, - typeof(File).GetTypeInfo().Assembly)) - .SetValue("log", new Action(Console.WriteLine)) - .SetValue("assert", new Action(Assert.True)) - .SetValue("equal", new Action(Assert.Equal)) - ; - } + public InteropTests() + { + _engine = new Engine(cfg => cfg.AllowClr( + typeof(Console).GetTypeInfo().Assembly, + typeof(File).GetTypeInfo().Assembly)) + .SetValue("log", new Action(Console.WriteLine)) + .SetValue("assert", new Action(Assert.True)) + .SetValue("equal", new Action(Assert.Equal)) + ; + } - void IDisposable.Dispose() - { - } + void IDisposable.Dispose() + { } -} +} \ No newline at end of file diff --git a/Jint.Tests.PublicInterface/JavaScriptExceptionTests.cs b/Jint.Tests.PublicInterface/JavaScriptExceptionTests.cs index 1365588c36..9bf5587fee 100644 --- a/Jint.Tests.PublicInterface/JavaScriptExceptionTests.cs +++ b/Jint.Tests.PublicInterface/JavaScriptExceptionTests.cs @@ -1,37 +1,36 @@ using Jint.Native; using Jint.Runtime; -namespace Jint.Tests.PublicInterface +namespace Jint.Tests.PublicInterface; + +public class JavaScriptExceptionTests { - public class JavaScriptExceptionTests + [Fact] + public void CanCreateAndThrowJavaScriptException() { - [Fact] - public void CanCreateAndThrowJavaScriptException() - { - var engine = new Engine(); + var engine = new Engine(); - engine.SetValue("throw1", () => - { - throw new JavaScriptException(engine.Intrinsics.Error, "message 1"); - }); + engine.SetValue("throw1", () => + { + throw new JavaScriptException(engine.Intrinsics.Error, "message 1"); + }); - engine.SetValue("throw2", () => - { - throw new JavaScriptException(new JsString("message 2")); - }); + engine.SetValue("throw2", () => + { + throw new JavaScriptException(new JsString("message 2")); + }); - Assert.Throws(() => - { - engine.Evaluate(@"throw1()"); - }); + Assert.Throws(() => + { + engine.Evaluate(@"throw1()"); + }); - var result1 = engine.Evaluate(@"try { throw1() } catch (e) { return e; }"); - var error1 = Assert.IsType(result1); - Assert.Equal("message 1", error1.Get("message").ToString()); + var result1 = engine.Evaluate(@"try { throw1() } catch (e) { return e; }"); + var error1 = Assert.IsType(result1); + Assert.Equal("message 1", error1.Get("message").ToString()); - var result2 = engine.Evaluate(@"try { throw2() } catch (e) { return e; }"); - var jsString = Assert.IsType(result2); - Assert.Equal("message 2", jsString.ToString()); - } + var result2 = engine.Evaluate(@"try { throw2() } catch (e) { return e; }"); + var jsString = Assert.IsType(result2); + Assert.Equal("message 2", jsString.ToString()); } -} +} \ No newline at end of file diff --git a/Jint.Tests/ReplaceCulture.cs b/Jint.Tests/ReplaceCulture.cs index 95c590b746..869cc5371d 100644 --- a/Jint.Tests/ReplaceCulture.cs +++ b/Jint.Tests/ReplaceCulture.cs @@ -5,42 +5,42 @@ using System.Reflection; using Xunit.Sdk; -namespace Jint.Tests +namespace Jint.Tests; + +/// +/// Replaces the current culture and UI culture for the test. +/// +[AttributeUsage(AttributeTargets.Method)] +public class ReplaceCultureAttribute : BeforeAfterTestAttribute { + private CultureInfo _originalCulture; + private CultureInfo _originalUICulture; + /// - /// Replaces the current culture and UI culture for the test. + /// Replaces the current culture and UI culture based on specified value. /// - [AttributeUsage(AttributeTargets.Method)] - public class ReplaceCultureAttribute : BeforeAfterTestAttribute + public ReplaceCultureAttribute(string currentCulture) : this(currentCulture, currentCulture) { - private CultureInfo _originalCulture; - private CultureInfo _originalUICulture; - - /// - /// Replaces the current culture and UI culture based on specified value. - /// - public ReplaceCultureAttribute(string currentCulture) : this(currentCulture, currentCulture) - { - } + } - /// - /// Replaces the current culture and UI culture based on specified values. - /// - public ReplaceCultureAttribute(string currentCulture, string currentUICulture) - { - Culture = new CultureInfo(currentCulture); - UICulture = new CultureInfo(currentUICulture); - } + /// + /// Replaces the current culture and UI culture based on specified values. + /// + public ReplaceCultureAttribute(string currentCulture, string currentUICulture) + { + Culture = new CultureInfo(currentCulture); + UICulture = new CultureInfo(currentUICulture); + } #if NETFRAMEWORK - /// - /// The for the test. Defaults to en-GB. - /// - /// - /// en-GB is used here as the default because en-US is equivalent to the InvariantCulture. We - /// want to be able to find bugs where we're accidentally relying on the Invariant instead of the - /// user's culture. - /// + /// + /// The for the test. Defaults to en-GB. + /// + /// + /// en-GB is used here as the default because en-US is equivalent to the InvariantCulture. We + /// want to be able to find bugs where we're accidentally relying on the Invariant instead of the + /// user's culture. + /// #else /// /// The for the test. Defaults to en-GB. @@ -51,43 +51,42 @@ public ReplaceCultureAttribute(string currentCulture, string currentUICulture) /// user's culture. /// #endif - public CultureInfo Culture { get; } + public CultureInfo Culture { get; } #if NETFRAMEWORK - /// - /// The for the test. Defaults to en-US. - /// + /// + /// The for the test. Defaults to en-US. + /// #else /// /// The for the test. Defaults to en-US. /// #endif - public CultureInfo UICulture { get; } + public CultureInfo UICulture { get; } - public override void Before(MethodInfo methodUnderTest) - { - _originalCulture = CultureInfo.CurrentCulture; - _originalUICulture = CultureInfo.CurrentUICulture; + public override void Before(MethodInfo methodUnderTest) + { + _originalCulture = CultureInfo.CurrentCulture; + _originalUICulture = CultureInfo.CurrentUICulture; #if NETFRAMEWORK - System.Threading.Thread.CurrentThread.CurrentCulture = Culture; - System.Threading.Thread.CurrentThread.CurrentUICulture = UICulture; + System.Threading.Thread.CurrentThread.CurrentCulture = Culture; + System.Threading.Thread.CurrentThread.CurrentUICulture = UICulture; #else CultureInfo.CurrentCulture = Culture; CultureInfo.CurrentUICulture = UICulture; #endif - } + } - public override void After(MethodInfo methodUnderTest) - { + public override void After(MethodInfo methodUnderTest) + { #if NETFRAMEWORK - System.Threading.Thread.CurrentThread.CurrentCulture = _originalCulture; - System.Threading.Thread.CurrentThread.CurrentUICulture = _originalUICulture; + System.Threading.Thread.CurrentThread.CurrentCulture = _originalCulture; + System.Threading.Thread.CurrentThread.CurrentUICulture = _originalUICulture; #else CultureInfo.CurrentCulture = _originalCulture; CultureInfo.CurrentUICulture = _originalUICulture; #endif - } } -} +} \ No newline at end of file diff --git a/Jint.Tests/RunnableInDebugOnlyAttribute.cs b/Jint.Tests/RunnableInDebugOnlyAttribute.cs index 1beaf112d4..2c974bc9d5 100644 --- a/Jint.Tests/RunnableInDebugOnlyAttribute.cs +++ b/Jint.Tests/RunnableInDebugOnlyAttribute.cs @@ -1,15 +1,14 @@ using System.Diagnostics; -namespace Jint.Tests +namespace Jint.Tests; + +public class RunnableInDebugOnlyAttribute : FactAttribute { - public class RunnableInDebugOnlyAttribute : FactAttribute + public RunnableInDebugOnlyAttribute() { - public RunnableInDebugOnlyAttribute() + if (!Debugger.IsAttached) { - if (!Debugger.IsAttached) - { - Skip = "Only running in interactive mode."; - } + Skip = "Only running in interactive mode."; } } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/CallStackTests.cs b/Jint.Tests/Runtime/CallStackTests.cs index 750a69db9e..10dd0c08c7 100644 --- a/Jint.Tests/Runtime/CallStackTests.cs +++ b/Jint.Tests/Runtime/CallStackTests.cs @@ -1,12 +1,12 @@ -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class CallStackTests { - public class CallStackTests + [Fact] + public void ShouldUnwindAfterCaughtException() { - [Fact] - public void ShouldUnwindAfterCaughtException() - { - var engine = new Engine(); - engine.Execute(@" + var engine = new Engine(); + engine.Execute(@" function thrower() { throw new Error('test'); @@ -20,15 +20,15 @@ function thrower() { } " - ); - Assert.Equal(0, engine.CallStack.Count); - } + ); + Assert.Equal(0, engine.CallStack.Count); + } - [Fact] - public void ShouldUnwindAfterCaughtExceptionNested() - { - var engine = new Engine(); - engine.Execute(@" + [Fact] + public void ShouldUnwindAfterCaughtExceptionNested() + { + var engine = new Engine(); + engine.Execute(@" function thrower2() { throw new Error('test'); @@ -47,7 +47,6 @@ function thrower1() { } "); - Assert.Equal(0, engine.CallStack.Count); - } + Assert.Equal(0, engine.CallStack.Count); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/ConstTests.cs b/Jint.Tests/Runtime/ConstTests.cs index e9e6efaf72..7f271ea123 100644 --- a/Jint.Tests/Runtime/ConstTests.cs +++ b/Jint.Tests/Runtime/ConstTests.cs @@ -1,21 +1,21 @@ -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class ConstTests { - public class ConstTests - { - private readonly Engine _engine; + private readonly Engine _engine; - public ConstTests() - { - _engine = new Engine() - .SetValue("log", new Action(Console.WriteLine)) - .SetValue("assert", new Action(Assert.True)) - .SetValue("equal", new Action(Assert.Equal)); - } + public ConstTests() + { + _engine = new Engine() + .SetValue("log", new Action(Console.WriteLine)) + .SetValue("assert", new Action(Assert.True)) + .SetValue("equal", new Action(Assert.Equal)); + } - [Fact] - public void ConstInsideIife() - { - _engine.Execute(@" + [Fact] + public void ConstInsideIife() + { + _engine.Execute(@" (function(){ const testVariable = 'test'; function render() { @@ -24,23 +24,23 @@ function render() { render(); })(); "); - } + } - [Fact] - public void ConstDestructuring() - { - _engine.Execute(@" + [Fact] + public void ConstDestructuring() + { + _engine.Execute(@" let obj = {}; for (var i = 0; i < 1; i++) { const { subElement } = obj; } "); - } + } - [Fact] - public void DestructuringWithFunctionArgReferenceInStrictMode() - { - _engine.Execute(@" + [Fact] + public void DestructuringWithFunctionArgReferenceInStrictMode() + { + _engine.Execute(@" 'use strict'; function tst(a) { let [let1, let2, let3] = a; @@ -60,6 +60,5 @@ function tst(a) { tst([1,2]) "); - } } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Converters/EnumsToStringConverter.cs b/Jint.Tests/Runtime/Converters/EnumsToStringConverter.cs index 74db86fedc..ebfe4de9c3 100644 --- a/Jint.Tests/Runtime/Converters/EnumsToStringConverter.cs +++ b/Jint.Tests/Runtime/Converters/EnumsToStringConverter.cs @@ -1,20 +1,19 @@ using Jint.Native; using Jint.Runtime.Interop; -namespace Jint.Tests.Runtime.Converters +namespace Jint.Tests.Runtime.Converters; + +public class EnumsToStringConverter : IObjectConverter { - public class EnumsToStringConverter : IObjectConverter + public bool TryConvert(Engine engine, object value, out JsValue result) { - public bool TryConvert(Engine engine, object value, out JsValue result) + if (value is Enum) { - if (value is Enum) - { - result = value.ToString(); - return true; - } - - result = JsValue.Null; - return false; + result = value.ToString(); + return true; } + + result = JsValue.Null; + return false; } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Converters/NegateBoolConverter.cs b/Jint.Tests/Runtime/Converters/NegateBoolConverter.cs index 1ebc7517e9..614105de9e 100644 --- a/Jint.Tests/Runtime/Converters/NegateBoolConverter.cs +++ b/Jint.Tests/Runtime/Converters/NegateBoolConverter.cs @@ -1,20 +1,19 @@ using Jint.Native; using Jint.Runtime.Interop; -namespace Jint.Tests.Runtime.Converters +namespace Jint.Tests.Runtime.Converters; + +public class NegateBoolConverter : IObjectConverter { - public class NegateBoolConverter : IObjectConverter + public bool TryConvert(Engine engine, object value, out JsValue result) { - public bool TryConvert(Engine engine, object value, out JsValue result) + if (value is bool b) { - if (value is bool b) - { - result = !b; - return true; - } - - result = JsValue.Null; - return false; + result = !b; + return true; } + + result = JsValue.Null; + return false; } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Debugger/BreakPointTests.cs b/Jint.Tests/Runtime/Debugger/BreakPointTests.cs index 130d0289c1..580abce8cb 100644 --- a/Jint.Tests/Runtime/Debugger/BreakPointTests.cs +++ b/Jint.Tests/Runtime/Debugger/BreakPointTests.cs @@ -1,338 +1,338 @@ using Jint.Runtime.Debugger; -namespace Jint.Tests.Runtime.Debugger +namespace Jint.Tests.Runtime.Debugger; + +public class BreakPointTests { - public class BreakPointTests + [Fact] + public void BreakLocationsCompareEqualityByValue() { - [Fact] - public void BreakLocationsCompareEqualityByValue() - { - var loc1 = new BreakLocation(42, 23); - var loc2 = new BreakLocation(42, 23); - var loc3 = new BreakLocation(17, 7); - - Assert.Equal(loc1, loc2); - Assert.True(loc1 == loc2); - Assert.True(loc2 != loc3); - Assert.False(loc1 != loc2); - Assert.False(loc2 == loc3); - } + var loc1 = new BreakLocation(42, 23); + var loc2 = new BreakLocation(42, 23); + var loc3 = new BreakLocation(17, 7); + + Assert.Equal(loc1, loc2); + Assert.True(loc1 == loc2); + Assert.True(loc2 != loc3); + Assert.False(loc1 != loc2); + Assert.False(loc2 == loc3); + } - [Fact] - public void BreakLocationsWithSourceCompareEqualityByValue() - { - var loc1 = new BreakLocation("script1", 42, 23); - var loc2 = new BreakLocation("script1", 42, 23); - var loc3 = new BreakLocation("script2", 42, 23); - - Assert.Equal(loc1, loc2); - Assert.True(loc1 == loc2); - Assert.True(loc2 != loc3); - Assert.False(loc1 != loc2); - Assert.False(loc2 == loc3); - } + [Fact] + public void BreakLocationsWithSourceCompareEqualityByValue() + { + var loc1 = new BreakLocation("script1", 42, 23); + var loc2 = new BreakLocation("script1", 42, 23); + var loc3 = new BreakLocation("script2", 42, 23); + + Assert.Equal(loc1, loc2); + Assert.True(loc1 == loc2); + Assert.True(loc2 != loc3); + Assert.False(loc1 != loc2); + Assert.False(loc2 == loc3); + } - [Fact] - public void BreakLocationsOptionalSourceEqualityComparer() - { - var script1 = new BreakLocation("script1", 42, 23); - var script2 = new BreakLocation("script2", 42, 23); - var script2b = new BreakLocation("script2", 44, 23); - var any = new BreakLocation(null, 42, 23); - - var comparer = new OptionalSourceBreakLocationEqualityComparer(); - Assert.True(comparer.Equals(script1, any)); - Assert.True(comparer.Equals(script2, any)); - Assert.False(comparer.Equals(script1, script2)); - Assert.False(comparer.Equals(script2, script2b)); - Assert.Equal(comparer.GetHashCode(script1), comparer.GetHashCode(any)); - Assert.Equal(comparer.GetHashCode(script1), comparer.GetHashCode(script2)); - Assert.NotEqual(comparer.GetHashCode(script2), comparer.GetHashCode(script2b)); - } + [Fact] + public void BreakLocationsOptionalSourceEqualityComparer() + { + var script1 = new BreakLocation("script1", 42, 23); + var script2 = new BreakLocation("script2", 42, 23); + var script2b = new BreakLocation("script2", 44, 23); + var any = new BreakLocation(null, 42, 23); + + var comparer = new OptionalSourceBreakLocationEqualityComparer(); + Assert.True(comparer.Equals(script1, any)); + Assert.True(comparer.Equals(script2, any)); + Assert.False(comparer.Equals(script1, script2)); + Assert.False(comparer.Equals(script2, script2b)); + Assert.Equal(comparer.GetHashCode(script1), comparer.GetHashCode(any)); + Assert.Equal(comparer.GetHashCode(script1), comparer.GetHashCode(script2)); + Assert.NotEqual(comparer.GetHashCode(script2), comparer.GetHashCode(script2b)); + } - [Fact] - public void BreakPointReplacesPreviousBreakPoint() - { - var engine = new Engine(options => options.DebugMode()); - - engine.Debugger.BreakPoints.Set(new BreakPoint(4, 5, "i === 1")); - Assert.Collection(engine.Debugger.BreakPoints, - breakPoint => - { - Assert.Equal(4, breakPoint.Location.Line); - Assert.Equal(5, breakPoint.Location.Column); - Assert.Equal("i === 1", breakPoint.Condition); - }); - - engine.Debugger.BreakPoints.Set(new BreakPoint(4, 5)); - Assert.Collection(engine.Debugger.BreakPoints, - breakPoint => - { - Assert.Equal(4, breakPoint.Location.Line); - Assert.Equal(5, breakPoint.Location.Column); - Assert.Equal(null, breakPoint.Condition); - }); - } + [Fact] + public void BreakPointReplacesPreviousBreakPoint() + { + var engine = new Engine(options => options.DebugMode()); - [Fact] - public void BreakPointRemovesBasedOnLocationEquality() - { - var engine = new Engine(options => options.DebugMode()); - - engine.Debugger.BreakPoints.Set(new BreakPoint(4, 5, "i === 1")); - engine.Debugger.BreakPoints.Set(new BreakPoint(5, 6, "j === 2")); - engine.Debugger.BreakPoints.Set(new BreakPoint(10, 7, "x > 5")); - Assert.Equal(3, engine.Debugger.BreakPoints.Count); - - engine.Debugger.BreakPoints.RemoveAt(new BreakLocation(null, 4, 5)); - engine.Debugger.BreakPoints.RemoveAt(new BreakLocation(null, 10, 7)); - - Assert.Collection(engine.Debugger.BreakPoints, - breakPoint => - { - Assert.Equal(5, breakPoint.Location.Line); - Assert.Equal(6, breakPoint.Location.Column); - Assert.Equal("j === 2", breakPoint.Condition); - }); - } + engine.Debugger.BreakPoints.Set(new BreakPoint(4, 5, "i === 1")); + Assert.Collection(engine.Debugger.BreakPoints, + breakPoint => + { + Assert.Equal(4, breakPoint.Location.Line); + Assert.Equal(5, breakPoint.Location.Column); + Assert.Equal("i === 1", breakPoint.Condition); + }); + + engine.Debugger.BreakPoints.Set(new BreakPoint(4, 5)); + Assert.Collection(engine.Debugger.BreakPoints, + breakPoint => + { + Assert.Equal(4, breakPoint.Location.Line); + Assert.Equal(5, breakPoint.Location.Column); + Assert.Equal(null, breakPoint.Condition); + }); + } - [Fact] - public void BreakPointContainsBasedOnLocationEquality() - { - var engine = new Engine(options => options.DebugMode()); + [Fact] + public void BreakPointRemovesBasedOnLocationEquality() + { + var engine = new Engine(options => options.DebugMode()); - engine.Debugger.BreakPoints.Set(new BreakPoint(4, 5, "i === 1")); - engine.Debugger.BreakPoints.Set(new BreakPoint(5, 6, "j === 2")); - engine.Debugger.BreakPoints.Set(new BreakPoint(10, 7, "x > 5")); - Assert.True(engine.Debugger.BreakPoints.Contains(new BreakLocation(null, 5, 6))); - Assert.False(engine.Debugger.BreakPoints.Contains(new BreakLocation(null, 8, 9))); - } + engine.Debugger.BreakPoints.Set(new BreakPoint(4, 5, "i === 1")); + engine.Debugger.BreakPoints.Set(new BreakPoint(5, 6, "j === 2")); + engine.Debugger.BreakPoints.Set(new BreakPoint(10, 7, "x > 5")); + Assert.Equal(3, engine.Debugger.BreakPoints.Count); - [Fact] - public void BreakPointBreaksAtPosition() - { - string script = @"let x = 1, y = 2; + engine.Debugger.BreakPoints.RemoveAt(new BreakLocation(null, 4, 5)); + engine.Debugger.BreakPoints.RemoveAt(new BreakLocation(null, 10, 7)); + + Assert.Collection(engine.Debugger.BreakPoints, + breakPoint => + { + Assert.Equal(5, breakPoint.Location.Line); + Assert.Equal(6, breakPoint.Location.Column); + Assert.Equal("j === 2", breakPoint.Condition); + }); + } + + [Fact] + public void BreakPointContainsBasedOnLocationEquality() + { + var engine = new Engine(options => options.DebugMode()); + + engine.Debugger.BreakPoints.Set(new BreakPoint(4, 5, "i === 1")); + engine.Debugger.BreakPoints.Set(new BreakPoint(5, 6, "j === 2")); + engine.Debugger.BreakPoints.Set(new BreakPoint(10, 7, "x > 5")); + Assert.True(engine.Debugger.BreakPoints.Contains(new BreakLocation(null, 5, 6))); + Assert.False(engine.Debugger.BreakPoints.Contains(new BreakLocation(null, 8, 9))); + } + + [Fact] + public void BreakPointBreaksAtPosition() + { + string script = @"let x = 1, y = 2; if (x === 1) { x++; y *= 2; }"; - var engine = new Engine(options => options.DebugMode()); - - bool didBreak = false; - engine.Debugger.Break += (sender, info) => - { - Assert.Equal(4, info.Location.Start.Line); - Assert.Equal(5, info.Location.Start.Column); - didBreak = true; - return StepMode.None; - }; - - engine.Debugger.BreakPoints.Set(new BreakPoint(4, 5)); - engine.Execute(script); - Assert.True(didBreak); - } + var engine = new Engine(options => options.DebugMode()); - [Fact] - public void BreakPointBreaksInCorrectSource() + bool didBreak = false; + engine.Debugger.Break += (sender, info) => { - string script1 = @"let x = 1, y = 2; + Assert.Equal(4, info.Location.Start.Line); + Assert.Equal(5, info.Location.Start.Column); + didBreak = true; + return StepMode.None; + }; + + engine.Debugger.BreakPoints.Set(new BreakPoint(4, 5)); + engine.Execute(script); + Assert.True(didBreak); + } + + [Fact] + public void BreakPointBreaksInCorrectSource() + { + string script1 = @"let x = 1, y = 2; if (x === 1) { x++; y *= 2; }"; - string script2 = @"function test(x) + string script2 = @"function test(x) { return x + 2; }"; - string script3 = @"const z = 3; + string script3 = @"const z = 3; test(z);"; - var engine = new Engine(options => { options.DebugMode(); }); - - engine.Debugger.BreakPoints.Set(new BreakPoint("script2", 3, 0)); - - bool didBreak = false; - engine.Debugger.Break += (sender, info) => - { - Assert.Equal("script2", info.Location.SourceFile); - Assert.Equal(3, info.Location.Start.Line); - Assert.Equal(0, info.Location.Start.Column); - didBreak = true; - return StepMode.None; - }; - - // We need to specify the source to the parser. - // And we need locations too (Jint specifies that in its default options) - engine.Execute(script1, "script1"); - Assert.False(didBreak); - - engine.Execute(script2, "script2"); - Assert.False(didBreak); + var engine = new Engine(options => { options.DebugMode(); }); - // Note that it's actually script3 that executes the function in script2 - // and triggers the breakpoint - engine.Execute(script3, "script3"); - Assert.True(didBreak); - } + engine.Debugger.BreakPoints.Set(new BreakPoint("script2", 3, 0)); - [Fact] - public void DebuggerStatementTriggersBreak() + bool didBreak = false; + engine.Debugger.Break += (sender, info) => { - string script = @"'dummy'; + Assert.Equal("script2", info.Location.SourceFile); + Assert.Equal(3, info.Location.Start.Line); + Assert.Equal(0, info.Location.Start.Column); + didBreak = true; + return StepMode.None; + }; + + // We need to specify the source to the parser. + // And we need locations too (Jint specifies that in its default options) + engine.Execute(script1, "script1"); + Assert.False(didBreak); + + engine.Execute(script2, "script2"); + Assert.False(didBreak); + + // Note that it's actually script3 that executes the function in script2 + // and triggers the breakpoint + engine.Execute(script3, "script3"); + Assert.True(didBreak); + } + + [Fact] + public void DebuggerStatementTriggersBreak() + { + string script = @"'dummy'; debugger; 'dummy';"; - var engine = new Engine(options => options - .DebugMode() - .DebuggerStatementHandling(DebuggerStatementHandling.Script)); + var engine = new Engine(options => options + .DebugMode() + .DebuggerStatementHandling(DebuggerStatementHandling.Script)); - bool didBreak = false; - engine.Debugger.Break += (sender, info) => - { - Assert.Equal(PauseType.DebuggerStatement, info.PauseType); - didBreak = true; - return StepMode.None; - }; + bool didBreak = false; + engine.Debugger.Break += (sender, info) => + { + Assert.Equal(PauseType.DebuggerStatement, info.PauseType); + didBreak = true; + return StepMode.None; + }; - engine.Execute(script); + engine.Execute(script); - Assert.True(didBreak); - } + Assert.True(didBreak); + } - [Fact] - public void DebuggerStatementDoesNotTriggerBreakWhenStepping() - { - string script = @"'dummy'; + [Fact] + public void DebuggerStatementDoesNotTriggerBreakWhenStepping() + { + string script = @"'dummy'; debugger; 'dummy';"; - var engine = new Engine(options => options - .DebugMode() - .DebuggerStatementHandling(DebuggerStatementHandling.Script) - .InitialStepMode(StepMode.Into)); + var engine = new Engine(options => options + .DebugMode() + .DebuggerStatementHandling(DebuggerStatementHandling.Script) + .InitialStepMode(StepMode.Into)); - bool didBreak = false; - int stepCount = 0; - engine.Debugger.Break += (sender, info) => - { - didBreak = true; - return StepMode.None; - }; + bool didBreak = false; + int stepCount = 0; + engine.Debugger.Break += (sender, info) => + { + didBreak = true; + return StepMode.None; + }; - engine.Debugger.Step += (sender, info) => - { - stepCount++; - return StepMode.Into; - }; + engine.Debugger.Step += (sender, info) => + { + stepCount++; + return StepMode.Into; + }; - engine.Execute(script); - Assert.Equal(3, stepCount); - Assert.False(didBreak); - } + engine.Execute(script); + Assert.Equal(3, stepCount); + Assert.False(didBreak); + } - [Fact] - public void DebuggerStatementDoesNotTriggerBreakWhenAtBreakPoint() - { - string script = @"'dummy'; + [Fact] + public void DebuggerStatementDoesNotTriggerBreakWhenAtBreakPoint() + { + string script = @"'dummy'; debugger; 'dummy';"; - var engine = new Engine(options => options - .DebugMode() - .DebuggerStatementHandling(DebuggerStatementHandling.Script) - .InitialStepMode(StepMode.None)); + var engine = new Engine(options => options + .DebugMode() + .DebuggerStatementHandling(DebuggerStatementHandling.Script) + .InitialStepMode(StepMode.None)); - int breakCount = 0; + int breakCount = 0; - engine.Debugger.BreakPoints.Set(new BreakPoint(2, 0)); + engine.Debugger.BreakPoints.Set(new BreakPoint(2, 0)); - engine.Debugger.Break += (sender, info) => - { - Assert.Equal(PauseType.Break, info.PauseType); - breakCount++; - return StepMode.None; - }; + engine.Debugger.Break += (sender, info) => + { + Assert.Equal(PauseType.Break, info.PauseType); + breakCount++; + return StepMode.None; + }; - engine.Execute(script); - Assert.Equal(1, breakCount); - } + engine.Execute(script); + Assert.Equal(1, breakCount); + } - [Fact] - public void BreakPointDoesNotTriggerBreakWhenStepping() - { - string script = @" + [Fact] + public void BreakPointDoesNotTriggerBreakWhenStepping() + { + string script = @" 'first breakpoint'; 'dummy'; 'second breakpoint';"; - var engine = new Engine(options => options - .DebugMode() - .InitialStepMode(StepMode.Into)); + var engine = new Engine(options => options + .DebugMode() + .InitialStepMode(StepMode.Into)); - bool didStep = true; - bool didBreak = true; + bool didStep = true; + bool didBreak = true; - engine.Debugger.BreakPoints.Set(new BreakPoint(2, 0)); - engine.Debugger.BreakPoints.Set(new BreakPoint(4, 0)); + engine.Debugger.BreakPoints.Set(new BreakPoint(2, 0)); + engine.Debugger.BreakPoints.Set(new BreakPoint(4, 0)); - engine.Debugger.Break += (sender, info) => + engine.Debugger.Break += (sender, info) => + { + didBreak = true; + // first breakpoint shouldn't cause us to get here, because we're stepping, + // but when we reach the second, we're running: + Assert.True(TestHelpers.ReachedLiteral(info, "second breakpoint")); + return StepMode.None; + }; + + engine.Debugger.Step += (sender, info) => + { + didStep = true; + if (TestHelpers.ReachedLiteral(info, "first breakpoint")) { - didBreak = true; - // first breakpoint shouldn't cause us to get here, because we're stepping, - // but when we reach the second, we're running: - Assert.True(TestHelpers.ReachedLiteral(info, "second breakpoint")); + // Run from here return StepMode.None; - }; + } + return StepMode.Into; + }; - engine.Debugger.Step += (sender, info) => - { - didStep = true; - if (TestHelpers.ReachedLiteral(info, "first breakpoint")) - { - // Run from here - return StepMode.None; - } - return StepMode.Into; - }; - - engine.Execute(script); - - Assert.True(didStep); - Assert.True(didBreak); - } + engine.Execute(script); - [Fact(Skip = "Non-source breakpoint is triggered before Statement, while debugger statement is now triggered by ExecuteInternal")] - public void DebuggerStatementAndBreakpointTriggerSingleBreak() - { - string script = @"'dummy'; + Assert.True(didStep); + Assert.True(didBreak); + } + + [Fact(Skip = "Non-source breakpoint is triggered before Statement, while debugger statement is now triggered by ExecuteInternal")] + public void DebuggerStatementAndBreakpointTriggerSingleBreak() + { + string script = @"'dummy'; debugger; 'dummy';"; - var engine = new Engine(options => options - .DebugMode() - .DebuggerStatementHandling(DebuggerStatementHandling.Script)); + var engine = new Engine(options => options + .DebugMode() + .DebuggerStatementHandling(DebuggerStatementHandling.Script)); - engine.Debugger.BreakPoints.Set(new BreakPoint(2, 0)); + engine.Debugger.BreakPoints.Set(new BreakPoint(2, 0)); - int breakTriggered = 0; - engine.Debugger.Break += (sender, info) => - { - breakTriggered++; - return StepMode.None; - }; + int breakTriggered = 0; + engine.Debugger.Break += (sender, info) => + { + breakTriggered++; + return StepMode.None; + }; - engine.Execute(script); + engine.Execute(script); - Assert.Equal(1, breakTriggered); - } + Assert.Equal(1, breakTriggered); + } - [Fact] - public void BreakpointOverridesStepOut() - { - string script = @"function test() + [Fact] + public void BreakpointOverridesStepOut() + { + string script = @"function test() { 'dummy'; 'source'; @@ -341,35 +341,35 @@ public void BreakpointOverridesStepOut() } test();"; - var engine = new Engine(options => options.DebugMode()); + var engine = new Engine(options => options.DebugMode()); - engine.Debugger.BreakPoints.Set(new BreakPoint(4, 0)); - engine.Debugger.BreakPoints.Set(new BreakPoint(6, 0)); + engine.Debugger.BreakPoints.Set(new BreakPoint(4, 0)); + engine.Debugger.BreakPoints.Set(new BreakPoint(6, 0)); - int step = 0; - engine.Debugger.Break += (sender, info) => + int step = 0; + engine.Debugger.Break += (sender, info) => + { + step++; + switch (step) { - step++; - switch (step) - { - case 1: - return StepMode.Out; - case 2: - Assert.True(info.ReachedLiteral("target")); - break; - } - return StepMode.None; - }; + case 1: + return StepMode.Out; + case 2: + Assert.True(info.ReachedLiteral("target")); + break; + } + return StepMode.None; + }; - engine.Execute(script); + engine.Execute(script); - Assert.Equal(2, step); - } + Assert.Equal(2, step); + } - [Fact] - public void ErrorInConditionalBreakpointLeavesCallStackAlone() - { - string script = @" + [Fact] + public void ErrorInConditionalBreakpointLeavesCallStackAlone() + { + string script = @" function foo() { let x = 0; @@ -381,93 +381,92 @@ function foo() foo(); "; - var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.Into)); + var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.Into)); - int stepsReached = 0; - int breakpointsReached = 0; + int stepsReached = 0; + int breakpointsReached = 0; - // This breakpoint will be hit: - engine.Debugger.BreakPoints.Set(new BreakPoint(6, 0, "x == 0")); - // This condition is an error (y is not defined). DebugHandler will - // treat it as an unmatched breakpoint: - engine.Debugger.BreakPoints.Set(new BreakPoint(7, 0, "y == 0")); + // This breakpoint will be hit: + engine.Debugger.BreakPoints.Set(new BreakPoint(6, 0, "x == 0")); + // This condition is an error (y is not defined). DebugHandler will + // treat it as an unmatched breakpoint: + engine.Debugger.BreakPoints.Set(new BreakPoint(7, 0, "y == 0")); - engine.Debugger.Step += (sender, info) => + engine.Debugger.Step += (sender, info) => + { + if (info.ReachedLiteral("before breakpoint")) { - if (info.ReachedLiteral("before breakpoint")) - { - Assert.Equal(1, engine.CallStack.Count); - stepsReached++; - return StepMode.None; - } - else if (info.ReachedLiteral("after breakpoint")) - { - Assert.Equal(1, engine.CallStack.Count); - stepsReached++; - return StepMode.None; - } - return StepMode.Into; - }; - - engine.Debugger.Break += (sender, info) => + Assert.Equal(1, engine.CallStack.Count); + stepsReached++; + return StepMode.None; + } + else if (info.ReachedLiteral("after breakpoint")) { - breakpointsReached++; - return StepMode.Into; - }; + Assert.Equal(1, engine.CallStack.Count); + stepsReached++; + return StepMode.None; + } + return StepMode.Into; + }; - engine.Execute(script); + engine.Debugger.Break += (sender, info) => + { + breakpointsReached++; + return StepMode.Into; + }; - Assert.Equal(1, breakpointsReached); - Assert.Equal(2, stepsReached); - } + engine.Execute(script); - private class SimpleHitConditionBreakPoint : BreakPoint - { - public SimpleHitConditionBreakPoint(int line, int column, string condition = null, - int? hitCondition = null) : base(line, column, condition) - { - HitCondition = hitCondition; - } + Assert.Equal(1, breakpointsReached); + Assert.Equal(2, stepsReached); + } - public int HitCount { get; set; } - public int? HitCondition { get; set; } + private class SimpleHitConditionBreakPoint : BreakPoint + { + public SimpleHitConditionBreakPoint(int line, int column, string condition = null, + int? hitCondition = null) : base(line, column, condition) + { + HitCondition = hitCondition; } - [Fact] - public void BreakPointCanBeExtended() - { - // More of a documentation than a required test, this shows the usefulness of BreakPoint being - // extensible - as a test, at least it ensures that it is. - var script = @" + public int HitCount { get; set; } + public int? HitCondition { get; set; } + } + + [Fact] + public void BreakPointCanBeExtended() + { + // More of a documentation than a required test, this shows the usefulness of BreakPoint being + // extensible - as a test, at least it ensures that it is. + var script = @" for (let i = 0; i < 10; i++) { 'breakpoint'; } "; - var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.None)); + var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.None)); - engine.Debugger.BreakPoints.Set( - new SimpleHitConditionBreakPoint(4, 4, condition: null, hitCondition: 5)); + engine.Debugger.BreakPoints.Set( + new SimpleHitConditionBreakPoint(4, 4, condition: null, hitCondition: 5)); - int numberOfBreaks = 0; - engine.Debugger.Break += (sender, info) => + int numberOfBreaks = 0; + engine.Debugger.Break += (sender, info) => + { + Assert.True(info.ReachedLiteral("breakpoint")); + var extendedBreakPoint = Assert.IsType(info.BreakPoint); + extendedBreakPoint.HitCount++; + if (extendedBreakPoint.HitCount == extendedBreakPoint.HitCondition) { - Assert.True(info.ReachedLiteral("breakpoint")); - var extendedBreakPoint = Assert.IsType(info.BreakPoint); - extendedBreakPoint.HitCount++; - if (extendedBreakPoint.HitCount == extendedBreakPoint.HitCondition) - { - // Here is where we would normally pause the execution. - // the breakpoint is hit for the fifth time, when i is 4 (off by one) - Assert.Equal(4, info.CurrentScopeChain[0].GetBindingValue("i").AsInteger()); - numberOfBreaks++; - } - return StepMode.None; - }; + // Here is where we would normally pause the execution. + // the breakpoint is hit for the fifth time, when i is 4 (off by one) + Assert.Equal(4, info.CurrentScopeChain[0].GetBindingValue("i").AsInteger()); + numberOfBreaks++; + } + return StepMode.None; + }; - engine.Execute(script); + engine.Execute(script); - Assert.Equal(1, numberOfBreaks); - } + Assert.Equal(1, numberOfBreaks); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Debugger/CallStackTests.cs b/Jint.Tests/Runtime/Debugger/CallStackTests.cs index deeabea3e3..1b8d980883 100644 --- a/Jint.Tests/Runtime/Debugger/CallStackTests.cs +++ b/Jint.Tests/Runtime/Debugger/CallStackTests.cs @@ -1,13 +1,13 @@ using Jint.Runtime.Debugger; -namespace Jint.Tests.Runtime.Debugger +namespace Jint.Tests.Runtime.Debugger; + +public class CallStackTests { - public class CallStackTests + [Fact] + public void IncludesFunctionNames() { - [Fact] - public void IncludesFunctionNames() - { - var script = @" + var script = @" function foo() { debugger; @@ -20,20 +20,20 @@ function bar() bar()"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Collection(info.CallStack, - frame => Assert.Equal("foo", frame.FunctionName), - frame => Assert.Equal("bar", frame.FunctionName), - frame => Assert.Equal("(anonymous)", frame.FunctionName) - ); - }); - } - - [Fact] - public void IncludesLocations() + TestHelpers.TestAtBreak(script, info => { - var script = @" + Assert.Collection(info.CallStack, + frame => Assert.Equal("foo", frame.FunctionName), + frame => Assert.Equal("bar", frame.FunctionName), + frame => Assert.Equal("(anonymous)", frame.FunctionName) + ); + }); + } + + [Fact] + public void IncludesLocations() + { + var script = @" function foo() { debugger; @@ -46,26 +46,26 @@ function bar() bar()"; - TestHelpers.TestAtBreak(script, info => - { - // The line numbers here may mislead - the positions are, as would be expected, - // at the position before the currently executing line, not the line after. - // Remember that Esprima (and hence Jint) line numbers are 1-based, not 0-based. - Assert.Collection(info.CallStack, - // "debugger;" - frame => Assert.Equal(Position.From(4, 0), frame.Location.Start), - // "foo();" - frame => Assert.Equal(Position.From(9, 0), frame.Location.Start), - // "bar();" - frame => Assert.Equal(Position.From(12, 0), frame.Location.Start) - ); - }); - } - - [Fact] - public void IncludesFunctionLocations() + TestHelpers.TestAtBreak(script, info => { - var script = @" + // The line numbers here may mislead - the positions are, as would be expected, + // at the position before the currently executing line, not the line after. + // Remember that Esprima (and hence Jint) line numbers are 1-based, not 0-based. + Assert.Collection(info.CallStack, + // "debugger;" + frame => Assert.Equal(Position.From(4, 0), frame.Location.Start), + // "foo();" + frame => Assert.Equal(Position.From(9, 0), frame.Location.Start), + // "bar();" + frame => Assert.Equal(Position.From(12, 0), frame.Location.Start) + ); + }); + } + + [Fact] + public void IncludesFunctionLocations() + { + var script = @" function foo() { debugger; @@ -78,24 +78,24 @@ function bar() bar()"; - TestHelpers.TestAtBreak(script, info => - { - // Remember that Esprima (and hence Jint) line numbers are 1-based, not 0-based. - Assert.Collection(info.CallStack, - // function foo() - frame => Assert.Equal(Position.From(2, 0), frame.FunctionLocation?.Start), - // function bar() - frame => Assert.Equal(Position.From(7, 0), frame.FunctionLocation?.Start), - // global - no function location - frame => Assert.Equal(null, frame.FunctionLocation?.Start) - ); - }); - } - - [Fact] - public void HasReturnValue() + TestHelpers.TestAtBreak(script, info => { - string script = @" + // Remember that Esprima (and hence Jint) line numbers are 1-based, not 0-based. + Assert.Collection(info.CallStack, + // function foo() + frame => Assert.Equal(Position.From(2, 0), frame.FunctionLocation?.Start), + // function bar() + frame => Assert.Equal(Position.From(7, 0), frame.FunctionLocation?.Start), + // global - no function location + frame => Assert.Equal(null, frame.FunctionLocation?.Start) + ); + }); + } + + [Fact] + public void HasReturnValue() + { + string script = @" function foo() { return 'result'; @@ -103,38 +103,38 @@ function foo() foo();"; - var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.Into)); + var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.Into)); - bool atReturn = false; - bool didCheckReturn = false; + bool atReturn = false; + bool didCheckReturn = false; - engine.Debugger.Step += (sender, info) => + engine.Debugger.Step += (sender, info) => + { + if (atReturn) { - if (atReturn) - { - Assert.NotNull(info.CurrentCallFrame.ReturnValue); - Assert.Equal("result", info.CurrentCallFrame.ReturnValue.AsString()); - didCheckReturn = true; - atReturn = false; - } + Assert.NotNull(info.CurrentCallFrame.ReturnValue); + Assert.Equal("result", info.CurrentCallFrame.ReturnValue.AsString()); + didCheckReturn = true; + atReturn = false; + } - if (info.CurrentNode is ReturnStatement) - { - // Step one further, and we should have the return value - atReturn = true; - } - return StepMode.Into; - }; + if (info.CurrentNode is ReturnStatement) + { + // Step one further, and we should have the return value + atReturn = true; + } + return StepMode.Into; + }; - engine.Execute(script); + engine.Execute(script); - Assert.True(didCheckReturn); - } + Assert.True(didCheckReturn); + } - [Fact] - public void HasThis() - { - string script = @" + [Fact] + public void HasThis() + { + string script = @" function Thing(name) { this.name = name; @@ -149,113 +149,113 @@ function Thing(name) car.test(); "; - TestHelpers.TestAtBreak(script, (engine, info) => - { - Assert.Collection(info.CallStack, - frame => Assert.Equal(engine.Realm.GlobalObject.Get("car"), frame.This), - frame => Assert.Equal(engine.Realm.GlobalObject, frame.This) - ); - }); - } - - [Fact] - public void NamesRegularFunction() + TestHelpers.TestAtBreak(script, (engine, info) => { - string script = @" + Assert.Collection(info.CallStack, + frame => Assert.Equal(engine.Realm.GlobalObject.Get("car"), frame.This), + frame => Assert.Equal(engine.Realm.GlobalObject, frame.This) + ); + }); + } + + [Fact] + public void NamesRegularFunction() + { + string script = @" function regularFunction() { debugger; } regularFunction();"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Equal("regularFunction", info.CurrentCallFrame.FunctionName); - }); - } - - [Fact] - public void NamesFunctionExpression() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Equal("regularFunction", info.CurrentCallFrame.FunctionName); + }); + } + + [Fact] + public void NamesFunctionExpression() + { + string script = @" const functionExpression = function() { debugger; } functionExpression()"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Equal("functionExpression", info.CurrentCallFrame.FunctionName); - }); - } - - [Fact] - public void NamesNamedFunctionExpression() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Equal("functionExpression", info.CurrentCallFrame.FunctionName); + }); + } + + [Fact] + public void NamesNamedFunctionExpression() + { + string script = @" const functionExpression = function namedFunction() { debugger; } functionExpression()"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Equal("namedFunction", info.CurrentCallFrame.FunctionName); - }); - } - - [Fact] - public void NamesArrowFunction() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Equal("namedFunction", info.CurrentCallFrame.FunctionName); + }); + } + + [Fact] + public void NamesArrowFunction() + { + string script = @" const arrowFunction = () => { debugger; } arrowFunction()"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Equal("arrowFunction", info.CurrentCallFrame.FunctionName); - }); - } - - [Fact] - public void NamesNewFunction() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Equal("arrowFunction", info.CurrentCallFrame.FunctionName); + }); + } + + [Fact] + public void NamesNewFunction() + { + string script = @" const newFunction = new Function('debugger;'); newFunction()"; - TestHelpers.TestAtBreak(script, info => - { - // Ideally, this should be "(anonymous)", but FunctionConstructor sets the "anonymous" name. - Assert.Equal("anonymous", info.CurrentCallFrame.FunctionName); - }); - } - - [Fact] - public void NamesMemberFunction() + TestHelpers.TestAtBreak(script, info => { - string script = @" + // Ideally, this should be "(anonymous)", but FunctionConstructor sets the "anonymous" name. + Assert.Equal("anonymous", info.CurrentCallFrame.FunctionName); + }); + } + + [Fact] + public void NamesMemberFunction() + { + string script = @" const obj = { memberFunction() { debugger; } }; obj.memberFunction()"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Equal("memberFunction", info.CurrentCallFrame.FunctionName); - }); - } - - [Fact] - public void NamesAnonymousFunction() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Equal("memberFunction", info.CurrentCallFrame.FunctionName); + }); + } + + [Fact] + public void NamesAnonymousFunction() + { + string script = @" (function() { debugger; }());"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Equal("(anonymous)", info.CurrentCallFrame.FunctionName); - }); - } - - [Fact] - public void NamesGetAccessor() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Equal("(anonymous)", info.CurrentCallFrame.FunctionName); + }); + } + + [Fact] + public void NamesGetAccessor() + { + string script = @" const obj = { get accessor() { @@ -265,16 +265,16 @@ get accessor() }; const x = obj.accessor;"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Equal("get accessor", info.CurrentCallFrame.FunctionName); - }); - } - - [Fact] - public void NamesSetAccessor() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Equal("get accessor", info.CurrentCallFrame.FunctionName); + }); + } + + [Fact] + public void NamesSetAccessor() + { + string script = @" const obj = { set accessor(value) { @@ -284,10 +284,9 @@ set accessor(value) }; obj.accessor = 42;"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Equal("set accessor", info.CurrentCallFrame.FunctionName); - }); - } + TestHelpers.TestAtBreak(script, info => + { + Assert.Equal("set accessor", info.CurrentCallFrame.FunctionName); + }); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Debugger/DebugHandlerTests.cs b/Jint.Tests/Runtime/Debugger/DebugHandlerTests.cs index a5fae38bfe..2d2a7c538f 100644 --- a/Jint.Tests/Runtime/Debugger/DebugHandlerTests.cs +++ b/Jint.Tests/Runtime/Debugger/DebugHandlerTests.cs @@ -3,46 +3,45 @@ #pragma warning disable 618 -namespace Jint.Tests.Runtime.Debugger +namespace Jint.Tests.Runtime.Debugger; + +public class DebugHandlerTests { - public class DebugHandlerTests + [Fact] + public void AvoidsPauseRecursion() { - [Fact] - public void AvoidsPauseRecursion() - { - // While the DebugHandler is in a paused state, it shouldn't relay further OnStep calls to Break/Step. - // Such calls would occur e.g. if Step/Break event handlers evaluate accessors. Failing to avoid - // reentrance in a multithreaded environment (e.g. using ManualResetEvent(Slim)) would cause - // a deadlock. - string script = @" + // While the DebugHandler is in a paused state, it shouldn't relay further OnStep calls to Break/Step. + // Such calls would occur e.g. if Step/Break event handlers evaluate accessors. Failing to avoid + // reentrance in a multithreaded environment (e.g. using ManualResetEvent(Slim)) would cause + // a deadlock. + string script = @" var obj = { get name() { 'fail'; return 'Smith'; } }; 'target'; "; - var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.Into)); + var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.Into)); - bool didPropertyAccess = false; + bool didPropertyAccess = false; + + engine.Debugger.Step += (sender, info) => + { + // We should never reach "fail", because the only way it's executed is from + // within this Step handler + Assert.False(info.ReachedLiteral("fail")); - engine.Debugger.Step += (sender, info) => + if (info.ReachedLiteral("target")) { - // We should never reach "fail", because the only way it's executed is from - // within this Step handler - Assert.False(info.ReachedLiteral("fail")); - - if (info.ReachedLiteral("target")) - { - var obj = info.CurrentScopeChain[0].GetBindingValue("obj") as ObjectInstance; - var prop = obj.GetOwnProperty("name"); - // This is where reentrance would occur: - var value = engine.Invoke(prop.Get); - didPropertyAccess = true; - } - return StepMode.Into; - }; - - engine.Execute(script); - - Assert.True(didPropertyAccess); - } + var obj = info.CurrentScopeChain[0].GetBindingValue("obj") as ObjectInstance; + var prop = obj.GetOwnProperty("name"); + // This is where reentrance would occur: + var value = engine.Invoke(prop.Get); + didPropertyAccess = true; + } + return StepMode.Into; + }; + + engine.Execute(script); + + Assert.True(didPropertyAccess); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Debugger/EvaluateTests.cs b/Jint.Tests/Runtime/Debugger/EvaluateTests.cs index d592492ed7..56a589fc90 100644 --- a/Jint.Tests/Runtime/Debugger/EvaluateTests.cs +++ b/Jint.Tests/Runtime/Debugger/EvaluateTests.cs @@ -2,14 +2,14 @@ using Jint.Runtime; using Jint.Runtime.Debugger; -namespace Jint.Tests.Runtime.Debugger +namespace Jint.Tests.Runtime.Debugger; + +public class EvaluateTests { - public class EvaluateTests + [Fact] + public void EvalutesInCurrentContext() { - [Fact] - public void EvalutesInCurrentContext() - { - var script = @" + var script = @" function test(x) { x *= 10; @@ -19,26 +19,26 @@ function test(x) test(5); "; - TestHelpers.TestAtBreak(script, (engine, info) => - { - var evaluated = engine.Debugger.Evaluate("x"); - Assert.IsType(evaluated); - Assert.Equal(50, evaluated.AsNumber()); - }); - } - - [Fact] - public void ThrowsIfNoCurrentContext() + TestHelpers.TestAtBreak(script, (engine, info) => { - var engine = new Engine(options => options.DebugMode()); - var exception = Assert.Throws(() => engine.Debugger.Evaluate("let x = 1;")); - Assert.Null(exception.InnerException); // Not a JavaScript or parser exception - } + var evaluated = engine.Debugger.Evaluate("x"); + Assert.IsType(evaluated); + Assert.Equal(50, evaluated.AsNumber()); + }); + } - [Fact] - public void ThrowsOnRuntimeError() - { - var script = @" + [Fact] + public void ThrowsIfNoCurrentContext() + { + var engine = new Engine(options => options.DebugMode()); + var exception = Assert.Throws(() => engine.Debugger.Evaluate("let x = 1;")); + Assert.Null(exception.InnerException); // Not a JavaScript or parser exception + } + + [Fact] + public void ThrowsOnRuntimeError() + { + var script = @" function test(x) { x *= 10; @@ -48,17 +48,17 @@ function test(x) test(5); "; - TestHelpers.TestAtBreak(script, (engine, info) => - { - var exception = Assert.Throws(() => engine.Debugger.Evaluate("y")); - Assert.IsType(exception.InnerException); - }); - } - - [Fact] - public void ThrowsOnExecutionError() + TestHelpers.TestAtBreak(script, (engine, info) => { - var script = @" + var exception = Assert.Throws(() => engine.Debugger.Evaluate("y")); + Assert.IsType(exception.InnerException); + }); + } + + [Fact] + public void ThrowsOnExecutionError() + { + var script = @" function test(x) { x *= 10; @@ -68,18 +68,18 @@ function test(x) test(5); "; - TestHelpers.TestAtBreak(script, (engine, info) => - { - var exception = Assert.Throws(() => - engine.Debugger.Evaluate("this is a syntax error")); - Assert.IsType(exception.InnerException); - }); - } - - [Fact] - public void RestoresStackAfterEvaluation() + TestHelpers.TestAtBreak(script, (engine, info) => { - var script = @" + var exception = Assert.Throws(() => + engine.Debugger.Evaluate("this is a syntax error")); + Assert.IsType(exception.InnerException); + }); + } + + [Fact] + public void RestoresStackAfterEvaluation() + { + var script = @" function throws() { throw new Error('Take this!'); @@ -94,22 +94,21 @@ function test(x) test(5); "; - TestHelpers.TestAtBreak(script, (engine, info) => - { - Assert.Equal(1, engine.CallStack.Count); - var frameBefore = engine.CallStack.Stack[0]; - - Assert.Throws(() => engine.Debugger.Evaluate("throws()")); - Assert.Equal(1, engine.CallStack.Count); - var frameAfter = engine.CallStack.Stack[0]; - // Stack frames and some of their properties are structs - can't check reference equality - // Besides, even if we could, it would be no guarantee. Neither is the following, but it'll do for now. - Assert.Equal(frameBefore.CallingExecutionContext.LexicalEnvironment, frameAfter.CallingExecutionContext.LexicalEnvironment); - Assert.Equal(frameBefore.Arguments, frameAfter.Arguments); - Assert.Equal(frameBefore.Expression, frameAfter.Expression); - Assert.Equal(frameBefore.Location, frameAfter.Location); - Assert.Equal(frameBefore.Function, frameAfter.Function); - }); - } + TestHelpers.TestAtBreak(script, (engine, info) => + { + Assert.Equal(1, engine.CallStack.Count); + var frameBefore = engine.CallStack.Stack[0]; + + Assert.Throws(() => engine.Debugger.Evaluate("throws()")); + Assert.Equal(1, engine.CallStack.Count); + var frameAfter = engine.CallStack.Stack[0]; + // Stack frames and some of their properties are structs - can't check reference equality + // Besides, even if we could, it would be no guarantee. Neither is the following, but it'll do for now. + Assert.Equal(frameBefore.CallingExecutionContext.LexicalEnvironment, frameAfter.CallingExecutionContext.LexicalEnvironment); + Assert.Equal(frameBefore.Arguments, frameAfter.Arguments); + Assert.Equal(frameBefore.Expression, frameAfter.Expression); + Assert.Equal(frameBefore.Location, frameAfter.Location); + Assert.Equal(frameBefore.Function, frameAfter.Function); + }); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Debugger/ScopeTests.cs b/Jint.Tests/Runtime/Debugger/ScopeTests.cs index 4a06320918..5768efe63d 100644 --- a/Jint.Tests/Runtime/Debugger/ScopeTests.cs +++ b/Jint.Tests/Runtime/Debugger/ScopeTests.cs @@ -1,53 +1,53 @@ using Jint.Native; using Jint.Runtime.Debugger; -namespace Jint.Tests.Runtime.Debugger +namespace Jint.Tests.Runtime.Debugger; + +public class ScopeTests { - public class ScopeTests + private static JsValue AssertOnlyScopeContains(DebugScopes scopes, string name, DebugScopeType scopeType) { - private static JsValue AssertOnlyScopeContains(DebugScopes scopes, string name, DebugScopeType scopeType) - { - var containingScope = Assert.Single(scopes, s => s.ScopeType == scopeType && s.BindingNames.Contains(name)); - Assert.DoesNotContain(scopes, s => s != containingScope && s.BindingNames.Contains(name)); + var containingScope = Assert.Single(scopes, s => s.ScopeType == scopeType && s.BindingNames.Contains(name)); + Assert.DoesNotContain(scopes, s => s != containingScope && s.BindingNames.Contains(name)); - return containingScope.GetBindingValue(name); - } + return containingScope.GetBindingValue(name); + } - private static void AssertScope(DebugScope actual, DebugScopeType expectedType, params string[] expectedBindingNames) + private static void AssertScope(DebugScope actual, DebugScopeType expectedType, params string[] expectedBindingNames) + { + Assert.Equal(expectedType, actual.ScopeType); + // Global scope will have a number of intrinsic bindings that are outside the scope [no pun] of these tests + if (actual.ScopeType != DebugScopeType.Global) { - Assert.Equal(expectedType, actual.ScopeType); - // Global scope will have a number of intrinsic bindings that are outside the scope [no pun] of these tests - if (actual.ScopeType != DebugScopeType.Global) - { - Assert.Equal(expectedBindingNames.Length, actual.BindingNames.Count); - } - foreach (string expectedName in expectedBindingNames) - { - Assert.Contains(expectedName, actual.BindingNames); - } + Assert.Equal(expectedBindingNames.Length, actual.BindingNames.Count); } - - [Fact] - public void AllowsInspectionOfUninitializedGlobalBindings() + foreach (string expectedName in expectedBindingNames) { - string script = @" + Assert.Contains(expectedName, actual.BindingNames); + } + } + + [Fact] + public void AllowsInspectionOfUninitializedGlobalBindings() + { + string script = @" debugger; const globalConstant = 'test'; let globalLet = 'test'; "; - TestHelpers.TestAtBreak(script, info => - { - // Uninitialized global block scoped ("script scoped") bindings return null (and, just as importantly, don't throw): - Assert.Null(info.CurrentScopeChain[0].GetBindingValue("globalConstant")); - Assert.Null(info.CurrentScopeChain[0].GetBindingValue("globalLet")); - }); - } - - [Fact] - public void AllowsInspectionOfUninitializedBlockBindings() + TestHelpers.TestAtBreak(script, info => { - string script = @" + // Uninitialized global block scoped ("script scoped") bindings return null (and, just as importantly, don't throw): + Assert.Null(info.CurrentScopeChain[0].GetBindingValue("globalConstant")); + Assert.Null(info.CurrentScopeChain[0].GetBindingValue("globalLet")); + }); + } + + [Fact] + public void AllowsInspectionOfUninitializedBlockBindings() + { + string script = @" function test() { debugger; @@ -57,61 +57,61 @@ function test() test(); "; - TestHelpers.TestAtBreak(script, info => - { - // Uninitialized block scoped bindings return null (and, just as importantly, don't throw): - Assert.Null(info.CurrentScopeChain[0].GetBindingValue("globalConstant")); - Assert.Null(info.CurrentScopeChain[0].GetBindingValue("globalLet")); - }); - } - - [Fact] - public void ScriptScopeIncludesGlobalConst() + TestHelpers.TestAtBreak(script, info => { - string script = @" + // Uninitialized block scoped bindings return null (and, just as importantly, don't throw): + Assert.Null(info.CurrentScopeChain[0].GetBindingValue("globalConstant")); + Assert.Null(info.CurrentScopeChain[0].GetBindingValue("globalLet")); + }); + } + + [Fact] + public void ScriptScopeIncludesGlobalConst() + { + string script = @" const globalConstant = 'test'; debugger; "; - TestHelpers.TestAtBreak(script, info => - { - var value = AssertOnlyScopeContains(info.CurrentScopeChain, "globalConstant", DebugScopeType.Script); - Assert.Equal("test", value.AsString()); - }); - } - - [Fact] - public void ScriptScopeIncludesGlobalLet() + TestHelpers.TestAtBreak(script, info => { - string script = @" + var value = AssertOnlyScopeContains(info.CurrentScopeChain, "globalConstant", DebugScopeType.Script); + Assert.Equal("test", value.AsString()); + }); + } + + [Fact] + public void ScriptScopeIncludesGlobalLet() + { + string script = @" let globalLet = 'test'; debugger;"; - TestHelpers.TestAtBreak(script, info => - { - var value = AssertOnlyScopeContains(info.CurrentScopeChain, "globalLet", DebugScopeType.Script); - Assert.Equal("test", value.AsString()); - }); - } - - [Fact] - public void GlobalScopeIncludesGlobalVar() + TestHelpers.TestAtBreak(script, info => { - string script = @" + var value = AssertOnlyScopeContains(info.CurrentScopeChain, "globalLet", DebugScopeType.Script); + Assert.Equal("test", value.AsString()); + }); + } + + [Fact] + public void GlobalScopeIncludesGlobalVar() + { + string script = @" var globalVar = 'test'; debugger;"; - TestHelpers.TestAtBreak(script, info => - { - var value = AssertOnlyScopeContains(info.CurrentScopeChain, "globalVar", DebugScopeType.Global); - Assert.Equal("test", value.AsString()); - }); - } - - [Fact] - public void TopLevelBlockScopeIsIdentified() + TestHelpers.TestAtBreak(script, info => { - string script = @" + var value = AssertOnlyScopeContains(info.CurrentScopeChain, "globalVar", DebugScopeType.Global); + Assert.Equal("test", value.AsString()); + }); + } + + [Fact] + public void TopLevelBlockScopeIsIdentified() + { + string script = @" function test() { const localConst = 'test'; @@ -119,18 +119,18 @@ function test() } test();"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Equal(3, info.CurrentScopeChain.Count); - Assert.Equal(DebugScopeType.Block, info.CurrentScopeChain[0].ScopeType); - Assert.True(info.CurrentScopeChain[0].IsTopLevel); - }); - } - - [Fact] - public void NonTopLevelBlockScopeIsIdentified() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Equal(3, info.CurrentScopeChain.Count); + Assert.Equal(DebugScopeType.Block, info.CurrentScopeChain[0].ScopeType); + Assert.True(info.CurrentScopeChain[0].IsTopLevel); + }); + } + + [Fact] + public void NonTopLevelBlockScopeIsIdentified() + { + string script = @" function test() { { @@ -140,19 +140,19 @@ function test() } test();"; - TestHelpers.TestAtBreak(script, info => - { - // We only have 3 scopes, because the function top level block scope is empty. - Assert.Equal(3, info.CurrentScopeChain.Count); - Assert.Equal(DebugScopeType.Block, info.CurrentScopeChain[0].ScopeType); - Assert.False(info.CurrentScopeChain[0].IsTopLevel); - }); - } - - [Fact] - public void BlockScopeIncludesLocalConst() + TestHelpers.TestAtBreak(script, info => { - string script = @" + // We only have 3 scopes, because the function top level block scope is empty. + Assert.Equal(3, info.CurrentScopeChain.Count); + Assert.Equal(DebugScopeType.Block, info.CurrentScopeChain[0].ScopeType); + Assert.False(info.CurrentScopeChain[0].IsTopLevel); + }); + } + + [Fact] + public void BlockScopeIncludesLocalConst() + { + string script = @" function test() { { @@ -162,16 +162,16 @@ function test() } test();"; - TestHelpers.TestAtBreak(script, info => - { - var value = AssertOnlyScopeContains(info.CurrentScopeChain, "localConst", DebugScopeType.Block); - Assert.Equal("test", value.AsString()); - }); - } - [Fact] - public void BlockScopeIncludesLocalLet() + TestHelpers.TestAtBreak(script, info => { - string script = @" + var value = AssertOnlyScopeContains(info.CurrentScopeChain, "localConst", DebugScopeType.Block); + Assert.Equal("test", value.AsString()); + }); + } + [Fact] + public void BlockScopeIncludesLocalLet() + { + string script = @" function test() { { @@ -181,17 +181,17 @@ function test() } test();"; - TestHelpers.TestAtBreak(script, info => - { - var value = AssertOnlyScopeContains(info.CurrentScopeChain, "localLet", DebugScopeType.Block); - Assert.Equal("test", value.AsString()); - }); - } - - [Fact] - public void LocalScopeIncludesLocalVar() + TestHelpers.TestAtBreak(script, info => { - string script = @" + var value = AssertOnlyScopeContains(info.CurrentScopeChain, "localLet", DebugScopeType.Block); + Assert.Equal("test", value.AsString()); + }); + } + + [Fact] + public void LocalScopeIncludesLocalVar() + { + string script = @" function test() { var localVar = 'test'; @@ -199,16 +199,16 @@ function test() } test();"; - TestHelpers.TestAtBreak(script, info => - { - AssertOnlyScopeContains(info.CurrentScopeChain, "localVar", DebugScopeType.Local); - }); - } - - [Fact] - public void LocalScopeIncludesBlockVar() + TestHelpers.TestAtBreak(script, info => { - string script = @" + AssertOnlyScopeContains(info.CurrentScopeChain, "localVar", DebugScopeType.Local); + }); + } + + [Fact] + public void LocalScopeIncludesBlockVar() + { + string script = @" function test() { debugger; @@ -218,48 +218,48 @@ function test() } test();"; - TestHelpers.TestAtBreak(script, info => - { - AssertOnlyScopeContains(info.CurrentScopeChain, "localVar", DebugScopeType.Local); - }); - } - - [Fact] - public void BlockScopedConstIsVisibleInsideBlock() + TestHelpers.TestAtBreak(script, info => { - string script = @" + AssertOnlyScopeContains(info.CurrentScopeChain, "localVar", DebugScopeType.Local); + }); + } + + [Fact] + public void BlockScopedConstIsVisibleInsideBlock() + { + string script = @" 'dummy statement'; { const blockConst = 'block'; debugger; // const isn't initialized until declaration }"; - TestHelpers.TestAtBreak(script, info => - { - AssertOnlyScopeContains(info.CurrentScopeChain, "blockConst", DebugScopeType.Block); - }); - } - - [Fact] - public void BlockScopedLetIsVisibleInsideBlock() + TestHelpers.TestAtBreak(script, info => { - string script = @" + AssertOnlyScopeContains(info.CurrentScopeChain, "blockConst", DebugScopeType.Block); + }); + } + + [Fact] + public void BlockScopedLetIsVisibleInsideBlock() + { + string script = @" 'dummy statement'; { let blockLet = 'block'; debugger; // let isn't initialized until declaration }"; - TestHelpers.TestAtBreak(script, info => - { - AssertOnlyScopeContains(info.CurrentScopeChain, "blockLet", DebugScopeType.Block); - }); - } - - [Fact] - public void HasCorrectScopeChainForFunction() + TestHelpers.TestAtBreak(script, info => { - string script = @" + AssertOnlyScopeContains(info.CurrentScopeChain, "blockLet", DebugScopeType.Block); + }); + } + + [Fact] + public void HasCorrectScopeChainForFunction() + { + string script = @" function add(a, b) { debugger; @@ -269,19 +269,19 @@ function add(a, b) const y = 2; const z = add(x, y);"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Collection(info.CurrentScopeChain, - scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a", "b"), - scope => AssertScope(scope, DebugScopeType.Script, "x", "y", "z"), - scope => AssertScope(scope, DebugScopeType.Global, "add")); - }); - } - - [Fact] - public void HasCorrectScopeChainForNestedFunction() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Collection(info.CurrentScopeChain, + scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a", "b"), + scope => AssertScope(scope, DebugScopeType.Script, "x", "y", "z"), + scope => AssertScope(scope, DebugScopeType.Global, "add")); + }); + } + + [Fact] + public void HasCorrectScopeChainForNestedFunction() + { + string script = @" function add(a, b) { function power(a) @@ -295,21 +295,21 @@ function power(a) const y = 2; const z = add(x, y);"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Collection(info.CurrentScopeChain, - scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a"), - // a, arguments shadowed by local - but still exist in this scope - scope => AssertScope(scope, DebugScopeType.Closure, "a", "arguments", "b", "power"), - scope => AssertScope(scope, DebugScopeType.Script, "x", "y", "z"), - scope => AssertScope(scope, DebugScopeType.Global, "add")); - }); - } - - [Fact] - public void HasCorrectScopeChainForBlock() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Collection(info.CurrentScopeChain, + scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a"), + // a, arguments shadowed by local - but still exist in this scope + scope => AssertScope(scope, DebugScopeType.Closure, "a", "arguments", "b", "power"), + scope => AssertScope(scope, DebugScopeType.Script, "x", "y", "z"), + scope => AssertScope(scope, DebugScopeType.Global, "add")); + }); + } + + [Fact] + public void HasCorrectScopeChainForBlock() + { + string script = @" function add(a, b) { if (a > 0) @@ -323,20 +323,20 @@ function add(a, b) const y = 2; const z = add(x, y);"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Collection(info.CurrentScopeChain, - scope => AssertScope(scope, DebugScopeType.Block, "y"), - scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a", "b"), - scope => AssertScope(scope, DebugScopeType.Script, "x", "y", "z"), // y is shadowed, but still in the scope - scope => AssertScope(scope, DebugScopeType.Global, "add")); - }); - } - - [Fact] - public void HasCorrectScopeChainForModule() + TestHelpers.TestAtBreak(script, info => { - string imported = @" + Assert.Collection(info.CurrentScopeChain, + scope => AssertScope(scope, DebugScopeType.Block, "y"), + scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a", "b"), + scope => AssertScope(scope, DebugScopeType.Script, "x", "y", "z"), // y is shadowed, but still in the scope + scope => AssertScope(scope, DebugScopeType.Global, "add")); + }); + } + + [Fact] + public void HasCorrectScopeChainForModule() + { + string imported = @" function add(a, b) { debugger; @@ -345,12 +345,12 @@ function add(a, b) export { add };"; - string main = @" + string main = @" import { add } from 'imported-module'; const x = 1; const y = 2; add(x, y);"; - TestHelpers.TestAtBreak(engine => + TestHelpers.TestAtBreak(engine => { engine.Modules.Add("imported-module", imported); engine.Modules.Add("main", main); @@ -363,12 +363,12 @@ function add(a, b) scope => AssertScope(scope, DebugScopeType.Module, "add"), scope => AssertScope(scope, DebugScopeType.Global)); }); - } + } - [Fact] - public void HasCorrectScopeChainForNestedBlock() - { - string script = @" + [Fact] + public void HasCorrectScopeChainForNestedBlock() + { + string script = @" function add(a, b) { if (a > 0) @@ -386,21 +386,21 @@ function add(a, b) const y = 2; const z = add(x, y);"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Collection(info.CurrentScopeChain, - scope => AssertScope(scope, DebugScopeType.Block, "x"), - scope => AssertScope(scope, DebugScopeType.Block, "y"), - scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a", "b"), - scope => AssertScope(scope, DebugScopeType.Script, "x", "y", "z"), // x, y are shadowed, but still in the scope - scope => AssertScope(scope, DebugScopeType.Global, "add")); - }); - } - - [Fact] - public void HasCorrectScopeChainForCatch() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Collection(info.CurrentScopeChain, + scope => AssertScope(scope, DebugScopeType.Block, "x"), + scope => AssertScope(scope, DebugScopeType.Block, "y"), + scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a", "b"), + scope => AssertScope(scope, DebugScopeType.Script, "x", "y", "z"), // x, y are shadowed, but still in the scope + scope => AssertScope(scope, DebugScopeType.Global, "add")); + }); + } + + [Fact] + public void HasCorrectScopeChainForCatch() + { + string script = @" function func() { let a = 1; @@ -415,20 +415,20 @@ function func() } func();"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Collection(info.CurrentScopeChain, - scope => AssertScope(scope, DebugScopeType.Catch, "error"), - scope => AssertScope(scope, DebugScopeType.Block, "a"), - scope => AssertScope(scope, DebugScopeType.Local, "arguments"), - scope => AssertScope(scope, DebugScopeType.Global, "func")); - }); - } - - [Fact] - public void HasCorrectScopeChainForWith() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Collection(info.CurrentScopeChain, + scope => AssertScope(scope, DebugScopeType.Catch, "error"), + scope => AssertScope(scope, DebugScopeType.Block, "a"), + scope => AssertScope(scope, DebugScopeType.Local, "arguments"), + scope => AssertScope(scope, DebugScopeType.Global, "func")); + }); + } + + [Fact] + public void HasCorrectScopeChainForWith() + { + string script = @" const obj = { a: 2, b: 4 }; with (obj) { @@ -436,20 +436,20 @@ public void HasCorrectScopeChainForWith() debugger; };"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Collection(info.CurrentScopeChain, - scope => AssertScope(scope, DebugScopeType.Block, "x"), - scope => AssertScope(scope, DebugScopeType.With, "a", "b"), - scope => AssertScope(scope, DebugScopeType.Script, "obj"), - scope => AssertScope(scope, DebugScopeType.Global)); - }); - } - - [Fact] - public void ScopeChainIncludesNonEmptyScopes() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Collection(info.CurrentScopeChain, + scope => AssertScope(scope, DebugScopeType.Block, "x"), + scope => AssertScope(scope, DebugScopeType.With, "a", "b"), + scope => AssertScope(scope, DebugScopeType.Script, "obj"), + scope => AssertScope(scope, DebugScopeType.Global)); + }); + } + + [Fact] + public void ScopeChainIncludesNonEmptyScopes() + { + string script = @" const x = 2; if (x > 0) { @@ -461,20 +461,20 @@ public void ScopeChainIncludesNonEmptyScopes() } }"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Collection(info.CurrentScopeChain, - scope => AssertScope(scope, DebugScopeType.Block, "z"), - scope => AssertScope(scope, DebugScopeType.Block, "y"), - scope => AssertScope(scope, DebugScopeType.Script, "x"), - scope => AssertScope(scope, DebugScopeType.Global)); - }); - } - - [Fact] - public void ScopeChainExcludesEmptyScopes() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Collection(info.CurrentScopeChain, + scope => AssertScope(scope, DebugScopeType.Block, "z"), + scope => AssertScope(scope, DebugScopeType.Block, "y"), + scope => AssertScope(scope, DebugScopeType.Script, "x"), + scope => AssertScope(scope, DebugScopeType.Global)); + }); + } + + [Fact] + public void ScopeChainExcludesEmptyScopes() + { + string script = @" const x = 2; if (x > 0) { @@ -485,19 +485,19 @@ public void ScopeChainExcludesEmptyScopes() } }"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Collection(info.CurrentScopeChain, - scope => AssertScope(scope, DebugScopeType.Block, "z"), - scope => AssertScope(scope, DebugScopeType.Script, "x"), - scope => AssertScope(scope, DebugScopeType.Global)); - }); - } - - [Fact] - public void ResolvesScopeChainsUpTheCallStack() + TestHelpers.TestAtBreak(script, info => { - string script = @" + Assert.Collection(info.CurrentScopeChain, + scope => AssertScope(scope, DebugScopeType.Block, "z"), + scope => AssertScope(scope, DebugScopeType.Script, "x"), + scope => AssertScope(scope, DebugScopeType.Global)); + }); + } + + [Fact] + public void ResolvesScopeChainsUpTheCallStack() + { + string script = @" const x = 1; function foo(a, c) { @@ -511,35 +511,35 @@ function bar(b) bar(x);"; - TestHelpers.TestAtBreak(script, info => - { - Assert.Collection(info.CallStack, - frame => Assert.Collection(frame.ScopeChain, - // in foo() - scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a", "c"), - scope => AssertScope(scope, DebugScopeType.Script, "x"), - scope => AssertScope(scope, DebugScopeType.Global, "foo", "bar") - ), - frame => Assert.Collection(frame.ScopeChain, - // in bar() - scope => AssertScope(scope, DebugScopeType.Local, "arguments", "b"), - scope => AssertScope(scope, DebugScopeType.Script, "x"), - scope => AssertScope(scope, DebugScopeType.Global, "foo", "bar") - ), - frame => Assert.Collection(frame.ScopeChain, - // in global - scope => AssertScope(scope, DebugScopeType.Script, "x"), - scope => AssertScope(scope, DebugScopeType.Global, "foo", "bar") - ) - ); - }); - } - - [Fact] - public void InspectsModuleScopedBindings() + TestHelpers.TestAtBreak(script, info => { - string main = @"const x = 1; debugger;"; - TestHelpers.TestAtBreak(engine => + Assert.Collection(info.CallStack, + frame => Assert.Collection(frame.ScopeChain, + // in foo() + scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a", "c"), + scope => AssertScope(scope, DebugScopeType.Script, "x"), + scope => AssertScope(scope, DebugScopeType.Global, "foo", "bar") + ), + frame => Assert.Collection(frame.ScopeChain, + // in bar() + scope => AssertScope(scope, DebugScopeType.Local, "arguments", "b"), + scope => AssertScope(scope, DebugScopeType.Script, "x"), + scope => AssertScope(scope, DebugScopeType.Global, "foo", "bar") + ), + frame => Assert.Collection(frame.ScopeChain, + // in global + scope => AssertScope(scope, DebugScopeType.Script, "x"), + scope => AssertScope(scope, DebugScopeType.Global, "foo", "bar") + ) + ); + }); + } + + [Fact] + public void InspectsModuleScopedBindings() + { + string main = @"const x = 1; debugger;"; + TestHelpers.TestAtBreak(engine => { engine.Modules.Add("main", main); engine.Modules.Import("main"); @@ -550,6 +550,5 @@ public void InspectsModuleScopedBindings() var value = AssertOnlyScopeContains(info.CurrentScopeChain, "x", DebugScopeType.Module); Assert.Equal(1, value.AsInteger()); }); - } } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Debugger/StepFlowTests.cs b/Jint.Tests/Runtime/Debugger/StepFlowTests.cs index b1676a1326..09327c00c5 100644 --- a/Jint.Tests/Runtime/Debugger/StepFlowTests.cs +++ b/Jint.Tests/Runtime/Debugger/StepFlowTests.cs @@ -1,31 +1,31 @@ using Jint.Runtime.Debugger; -namespace Jint.Tests.Runtime.Debugger +namespace Jint.Tests.Runtime.Debugger; + +public class StepFlowTests { - public class StepFlowTests + private List CollectStepNodes(string script) { - private List CollectStepNodes(string script) - { - var engine = new Engine(options => options - .DebugMode() - .InitialStepMode(StepMode.Into)); + var engine = new Engine(options => options + .DebugMode() + .InitialStepMode(StepMode.Into)); - var nodes = new List(); - engine.Debugger.Step += (sender, info) => - { - nodes.Add(info.CurrentNode); - return StepMode.Into; - }; + var nodes = new List(); + engine.Debugger.Step += (sender, info) => + { + nodes.Add(info.CurrentNode); + return StepMode.Into; + }; - engine.Execute(script); + engine.Execute(script); - return nodes; - } + return nodes; + } - [Fact] - public void StepsThroughWhileLoop() - { - var script = @" + [Fact] + public void StepsThroughWhileLoop() + { + var script = @" let x = 0; while (x < 2) { @@ -33,23 +33,23 @@ public void StepsThroughWhileLoop() } "; - var nodes = CollectStepNodes(script); - - Assert.Collection(nodes, - node => Assert.IsType(node), // let x = 0; - node => Assert.IsType(node), // while ... - node => Assert.IsType(node), // x < 2 - node => Assert.IsType(node), // x++; - node => Assert.IsType(node), // x < 2 - node => Assert.IsType(node), // x++; - node => Assert.IsType(node) // x < 2 (false) - ); - } + var nodes = CollectStepNodes(script); + + Assert.Collection(nodes, + node => Assert.IsType(node), // let x = 0; + node => Assert.IsType(node), // while ... + node => Assert.IsType(node), // x < 2 + node => Assert.IsType(node), // x++; + node => Assert.IsType(node), // x < 2 + node => Assert.IsType(node), // x++; + node => Assert.IsType(node) // x < 2 (false) + ); + } - [Fact] - public void StepsThroughDoWhileLoop() - { - var script = @" + [Fact] + public void StepsThroughDoWhileLoop() + { + var script = @" let x = 0; do { @@ -58,47 +58,47 @@ public void StepsThroughDoWhileLoop() while (x < 2) "; - var nodes = CollectStepNodes(script); + var nodes = CollectStepNodes(script); - Assert.Collection(nodes, - node => Assert.IsType(node), // let x = 0; - node => Assert.IsType(node), // do ... - node => Assert.IsType(node), // x++; - node => Assert.IsType(node), // x < 2 - node => Assert.IsType(node), // x++; - node => Assert.IsType(node) // x < 2 (false) - ); - } + Assert.Collection(nodes, + node => Assert.IsType(node), // let x = 0; + node => Assert.IsType(node), // do ... + node => Assert.IsType(node), // x++; + node => Assert.IsType(node), // x < 2 + node => Assert.IsType(node), // x++; + node => Assert.IsType(node) // x < 2 (false) + ); + } - [Fact] - public void StepsThroughForLoop() - { - var script = @" + [Fact] + public void StepsThroughForLoop() + { + var script = @" for (let x = 0; x < 2; x++) { 'dummy'; } "; - var nodes = CollectStepNodes(script); - - Assert.Collection(nodes, - node => Assert.IsType(node), // for ... - node => Assert.IsType(node), // let x = 0 - node => Assert.IsType(node), // x < 2 - node => Assert.True(node.IsLiteral("dummy")), // 'dummy'; - node => Assert.IsType(node), // x++; - node => Assert.IsType(node), // x < 2 - node => Assert.True(node.IsLiteral("dummy")), // 'dummy'; - node => Assert.IsType(node), // x++; - node => Assert.IsType(node) // x < 2 (false) - ); - } + var nodes = CollectStepNodes(script); + + Assert.Collection(nodes, + node => Assert.IsType(node), // for ... + node => Assert.IsType(node), // let x = 0 + node => Assert.IsType(node), // x < 2 + node => Assert.True(node.IsLiteral("dummy")), // 'dummy'; + node => Assert.IsType(node), // x++; + node => Assert.IsType(node), // x < 2 + node => Assert.True(node.IsLiteral("dummy")), // 'dummy'; + node => Assert.IsType(node), // x++; + node => Assert.IsType(node) // x < 2 (false) + ); + } - [Fact] - public void StepsThroughForOfLoop() - { - var script = @" + [Fact] + public void StepsThroughForOfLoop() + { + var script = @" const arr = [1, 2]; for (const item of arr) { @@ -106,22 +106,22 @@ public void StepsThroughForOfLoop() } "; - var nodes = CollectStepNodes(script); + var nodes = CollectStepNodes(script); - Assert.Collection(nodes, - node => Assert.IsType(node), // let arr = [1, 2]; - node => Assert.IsType(node), // for ... - node => Assert.IsType(node), // item - node => Assert.True(node.IsLiteral("dummy")), // 'dummy'; - node => Assert.IsType(node), // item - node => Assert.True(node.IsLiteral("dummy")) // 'dummy'; - ); - } + Assert.Collection(nodes, + node => Assert.IsType(node), // let arr = [1, 2]; + node => Assert.IsType(node), // for ... + node => Assert.IsType(node), // item + node => Assert.True(node.IsLiteral("dummy")), // 'dummy'; + node => Assert.IsType(node), // item + node => Assert.True(node.IsLiteral("dummy")) // 'dummy'; + ); + } - [Fact] - public void StepsThroughForInLoop() - { - var script = @" + [Fact] + public void StepsThroughForInLoop() + { + var script = @" const obj = { x: 1, y: 2 }; for (const key in obj) { @@ -129,22 +129,22 @@ public void StepsThroughForInLoop() } "; - var nodes = CollectStepNodes(script); + var nodes = CollectStepNodes(script); - Assert.Collection(nodes, - node => Assert.IsType(node), // let obj = { x: 1, y: 2 }; - node => Assert.IsType(node), // for ... - node => Assert.IsType(node), // key - node => Assert.IsType(node), // 'dummy'; - node => Assert.IsType(node), // key - node => Assert.IsType(node) // 'dummy'; - ); - } + Assert.Collection(nodes, + node => Assert.IsType(node), // let obj = { x: 1, y: 2 }; + node => Assert.IsType(node), // for ... + node => Assert.IsType(node), // key + node => Assert.IsType(node), // 'dummy'; + node => Assert.IsType(node), // key + node => Assert.IsType(node) // 'dummy'; + ); + } - [Fact] - public void StepsThroughConstructor() - { - var script = @" + [Fact] + public void StepsThroughConstructor() + { + var script = @" class Test { constructor() @@ -156,21 +156,21 @@ class Test 'after construction'; "; - var nodes = CollectStepNodes(script); + var nodes = CollectStepNodes(script); - Assert.Collection(nodes, - node => Assert.IsType(node), // class Test - node => Assert.IsType(node), // new Test(); - node => Assert.True(node.IsLiteral("in constructor")), // 'in constructor()' - node => Assert.Null(node), // return point - node => Assert.True(node.IsLiteral("after construction")) - ); - } + Assert.Collection(nodes, + node => Assert.IsType(node), // class Test + node => Assert.IsType(node), // new Test(); + node => Assert.True(node.IsLiteral("in constructor")), // 'in constructor()' + node => Assert.Null(node), // return point + node => Assert.True(node.IsLiteral("after construction")) + ); + } - [Fact] - public void SkipsFunctionBody() - { - var script = @" + [Fact] + public void SkipsFunctionBody() + { + var script = @" function test() { 'dummy'; @@ -178,20 +178,20 @@ function test() test(); "; - var nodes = CollectStepNodes(script); + var nodes = CollectStepNodes(script); - Assert.Collection(nodes, - node => Assert.IsType(node), // function(test) ...; - node => Assert.IsType(node), // test(); - node => Assert.True(node.IsLiteral("dummy")), // 'dummy'; - node => Assert.Null(node) // return point - ); - } + Assert.Collection(nodes, + node => Assert.IsType(node), // function(test) ...; + node => Assert.IsType(node), // test(); + node => Assert.True(node.IsLiteral("dummy")), // 'dummy'; + node => Assert.Null(node) // return point + ); + } - [Fact] - public void SkipsReturnPointOfImplicitConstructor() - { - var script = @" + [Fact] + public void SkipsReturnPointOfImplicitConstructor() + { + var script = @" class Test { } @@ -199,82 +199,81 @@ class Test 'dummy'; "; - var nodes = CollectStepNodes(script); - Assert.Collection(nodes, - node => Assert.IsType(node), // class Test - node => Assert.IsType(node), // new Test(); - node => Assert.True(node.IsLiteral("dummy")) // 'dummy'; - ); - } + var nodes = CollectStepNodes(script); + Assert.Collection(nodes, + node => Assert.IsType(node), // class Test + node => Assert.IsType(node), // new Test(); + node => Assert.True(node.IsLiteral("dummy")) // 'dummy'; + ); + } - [Fact] - public void StepIntoNamedFunctionCalls() - { - var script = @" + [Fact] + public void StepIntoNamedFunctionCalls() + { + var script = @" function a( ) { return 2; } function b(l) { return l + a(); } function c( ) { return b(3) + a(); } let res = c(); "; - var steps = StepIntoScript(script); - Assert.Collection(steps, - step => Assert.Equal("function c( ) { »return b(3) + a(); }", step), - step => Assert.Equal("function b(l) { »return l + a(); }", step), - step => Assert.Equal("function a( ) { »return 2; }", step), - step => Assert.Equal("function a( ) { return 2; }»", step), - step => Assert.Equal("function b(l) { return l + a(); }»", step), - step => Assert.Equal("function a( ) { »return 2; }", step), - step => Assert.Equal("function a( ) { return 2; }»", step), - step => Assert.Equal("function c( ) { return b(3) + a(); }»", step)); - } + var steps = StepIntoScript(script); + Assert.Collection(steps, + step => Assert.Equal("function c( ) { »return b(3) + a(); }", step), + step => Assert.Equal("function b(l) { »return l + a(); }", step), + step => Assert.Equal("function a( ) { »return 2; }", step), + step => Assert.Equal("function a( ) { return 2; }»", step), + step => Assert.Equal("function b(l) { return l + a(); }»", step), + step => Assert.Equal("function a( ) { »return 2; }", step), + step => Assert.Equal("function a( ) { return 2; }»", step), + step => Assert.Equal("function c( ) { return b(3) + a(); }»", step)); + } - [Fact] - public void StepIntoArrowFunctionCalls() - { - var script = @" + [Fact] + public void StepIntoArrowFunctionCalls() + { + var script = @" const a = ( ) => 2; const b = (l) => l + a(); const c = ( ) => b(3) + a(); let res = c(); "; - var steps = StepIntoScript(script); - Assert.Collection(steps, - step => Assert.Equal("const c = ( ) => »b(3) + a();", step), - step => Assert.Equal("const b = (l) => »l + a();", step), - step => Assert.Equal("const a = ( ) => »2;", step), - step => Assert.Equal("const a = ( ) => 2»;", step), - step => Assert.Equal("const b = (l) => l + a()»;", step), - step => Assert.Equal("const a = ( ) => »2;", step), - step => Assert.Equal("const a = ( ) => 2»;", step), - step => Assert.Equal("const c = ( ) => b(3) + a()»;", step)); - } + var steps = StepIntoScript(script); + Assert.Collection(steps, + step => Assert.Equal("const c = ( ) => »b(3) + a();", step), + step => Assert.Equal("const b = (l) => »l + a();", step), + step => Assert.Equal("const a = ( ) => »2;", step), + step => Assert.Equal("const a = ( ) => 2»;", step), + step => Assert.Equal("const b = (l) => l + a()»;", step), + step => Assert.Equal("const a = ( ) => »2;", step), + step => Assert.Equal("const a = ( ) => 2»;", step), + step => Assert.Equal("const c = ( ) => b(3) + a()»;", step)); + } + + private List StepIntoScript(string script) + { + var engine = new Engine(options => options + .DebugMode() + .InitialStepMode(StepMode.Into)); + + var stepStatements = new List(); + var scriptLines = script.Replace("\r\n", "\n").Replace("\r", "\n").Split('\n'); + engine.Debugger.Step += (sender, information) => + { + if (information.CurrentNode is not VariableDeclaration && information.CurrentNode is not FunctionDeclaration) + OutputPosition(information.Location); + return StepMode.Into; + }; + + engine.Execute(script); + return stepStatements; - private List StepIntoScript(string script) + void OutputPosition(in SourceLocation location) { - var engine = new Engine(options => options - .DebugMode() - .InitialStepMode(StepMode.Into)); - - var stepStatements = new List(); - var scriptLines = script.Replace("\r\n", "\n").Replace("\r", "\n").Split('\n'); - engine.Debugger.Step += (sender, information) => - { - if (information.CurrentNode is not VariableDeclaration && information.CurrentNode is not FunctionDeclaration) - OutputPosition(information.Location); - return StepMode.Into; - }; - - engine.Execute(script); - return stepStatements; - - void OutputPosition(in SourceLocation location) - { - var line = scriptLines[location.Start.Line - 1]; - var withPositionIndicator = string.Concat(line.Substring(0, location.Start.Column), "»", line.Substring(location.Start.Column)); - stepStatements.Add(withPositionIndicator.TrimEnd()); - } + var line = scriptLines[location.Start.Line - 1]; + var withPositionIndicator = string.Concat(line.Substring(0, location.Start.Column), "»", line.Substring(location.Start.Column)); + stepStatements.Add(withPositionIndicator.TrimEnd()); } } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Debugger/StepModeTests.cs b/Jint.Tests/Runtime/Debugger/StepModeTests.cs index e55788528f..db654380d8 100644 --- a/Jint.Tests/Runtime/Debugger/StepModeTests.cs +++ b/Jint.Tests/Runtime/Debugger/StepModeTests.cs @@ -1,60 +1,60 @@ using Jint.Runtime.Debugger; -namespace Jint.Tests.Runtime.Debugger +namespace Jint.Tests.Runtime.Debugger; + +public class StepModeTests { - public class StepModeTests + /// + /// Helper method to keep tests independent of line numbers, columns or other arbitrary assertions on + /// the current statement. Steps through script with StepMode.Into until it reaches literal statement + /// (or directive) 'source'. Then counts the steps needed to reach 'target' using the indicated StepMode. + /// + /// Script used as basis for test + /// StepMode to use from source to target + /// Number of steps from source to target + private static int StepsFromSourceToTarget(string script, StepMode stepMode) { - /// - /// Helper method to keep tests independent of line numbers, columns or other arbitrary assertions on - /// the current statement. Steps through script with StepMode.Into until it reaches literal statement - /// (or directive) 'source'. Then counts the steps needed to reach 'target' using the indicated StepMode. - /// - /// Script used as basis for test - /// StepMode to use from source to target - /// Number of steps from source to target - private static int StepsFromSourceToTarget(string script, StepMode stepMode) + var engine = new Engine(options => options + .DebugMode() + .InitialStepMode(StepMode.Into) + .DebuggerStatementHandling(DebuggerStatementHandling.Script)); + + int steps = 0; + bool sourceReached = false; + bool targetReached = false; + engine.Debugger.Step += (sender, info) => { - var engine = new Engine(options => options - .DebugMode() - .InitialStepMode(StepMode.Into) - .DebuggerStatementHandling(DebuggerStatementHandling.Script)); - - int steps = 0; - bool sourceReached = false; - bool targetReached = false; - engine.Debugger.Step += (sender, info) => + if (sourceReached) { - if (sourceReached) - { - steps++; - if (info.ReachedLiteral("target")) - { - // Stop stepping - targetReached = true; - return StepMode.None; - } - return stepMode; - } - else if (info.ReachedLiteral("source")) + steps++; + if (info.ReachedLiteral("target")) { - sourceReached = true; - return stepMode; + // Stop stepping + targetReached = true; + return StepMode.None; } - return StepMode.Into; - }; + return stepMode; + } + else if (info.ReachedLiteral("source")) + { + sourceReached = true; + return stepMode; + } + return StepMode.Into; + }; - engine.Execute(script); + engine.Execute(script); - // Make sure we actually reached the target - Assert.True(targetReached); + // Make sure we actually reached the target + Assert.True(targetReached); - return steps; - } + return steps; + } - [Fact] - public void StepsIntoRegularFunctionCall() - { - var script = @" + [Fact] + public void StepsIntoRegularFunctionCall() + { + var script = @" 'source'; test(); // first step function test() @@ -62,13 +62,13 @@ function test() 'target'; // second step }"; - Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Into)); - } + Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Into)); + } - [Fact] - public void StepsOverRegularFunctionCall() - { - var script = @" + [Fact] + public void StepsOverRegularFunctionCall() + { + var script = @" 'source'; test(); 'target'; @@ -77,13 +77,13 @@ function test() 'dummy'; }"; - Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Over)); - } + Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Over)); + } - [Fact] - public void StepsOutOfRegularFunctionCall() - { - var script = @" + [Fact] + public void StepsOutOfRegularFunctionCall() + { + var script = @" test(); 'target'; @@ -93,13 +93,13 @@ function test() 'dummy'; }"; - Assert.Equal(1, StepsFromSourceToTarget(script, StepMode.Out)); - } + Assert.Equal(1, StepsFromSourceToTarget(script, StepMode.Out)); + } - [Fact] - public void StepsIntoMemberFunctionCall() - { - var script = @" + [Fact] + public void StepsIntoMemberFunctionCall() + { + var script = @" const obj = { test() { @@ -109,13 +109,13 @@ public void StepsIntoMemberFunctionCall() 'source'; obj.test(); // first step"; - Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Into)); - } + Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Into)); + } - [Fact] - public void StepsOverMemberFunctionCall() - { - var script = @" + [Fact] + public void StepsOverMemberFunctionCall() + { + var script = @" const obj = { test() { @@ -126,13 +126,13 @@ public void StepsOverMemberFunctionCall() obj.test(); 'target';"; - Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Over)); - } + Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Over)); + } - [Fact] - public void StepsOutOfMemberFunctionCall() - { - var script = @" + [Fact] + public void StepsOutOfMemberFunctionCall() + { + var script = @" const obj = { test() { @@ -143,13 +143,13 @@ public void StepsOutOfMemberFunctionCall() obj.test(); 'target';"; - Assert.Equal(1, StepsFromSourceToTarget(script, StepMode.Out)); - } + Assert.Equal(1, StepsFromSourceToTarget(script, StepMode.Out)); + } - [Fact] - public void StepsIntoCallExpression() - { - var script = @" + [Fact] + public void StepsIntoCallExpression() + { + var script = @" function test() { 'target'; // second step @@ -158,13 +158,13 @@ function test() 'source'; const x = test(); // first step"; - Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Into)); - } + Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Into)); + } - [Fact] - public void StepsOverCallExpression() - { - var script = @" + [Fact] + public void StepsOverCallExpression() + { + var script = @" function test() { 'dummy'; @@ -174,13 +174,13 @@ function test() const x = test(); 'target';"; - Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Over)); - } + Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Over)); + } - [Fact] - public void StepsOutOfCallExpression() - { - var script = @" + [Fact] + public void StepsOutOfCallExpression() + { + var script = @" function test() { 'source'; @@ -190,13 +190,13 @@ function test() const x = test(); 'target';"; - Assert.Equal(1, StepsFromSourceToTarget(script, StepMode.Out)); - } + Assert.Equal(1, StepsFromSourceToTarget(script, StepMode.Out)); + } - [Fact] - public void StepsIntoGetAccessor() - { - var script = @" + [Fact] + public void StepsIntoGetAccessor() + { + var script = @" const obj = { get test() { @@ -207,13 +207,13 @@ get test() 'source'; const x = obj.test; // first step"; - Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Into)); - } + Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Into)); + } - [Fact] - public void StepsOverGetAccessor() - { - var script = @" + [Fact] + public void StepsOverGetAccessor() + { + var script = @" const obj = { get test() { @@ -224,13 +224,13 @@ get test() const x = obj.test; 'target';"; - Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Over)); - } + Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Over)); + } - [Fact] - public void StepsOutOfGetAccessor() - { - var script = @" + [Fact] + public void StepsOutOfGetAccessor() + { + var script = @" const obj = { get test() { @@ -242,13 +242,13 @@ get test() const x = obj.test; 'target';"; - Assert.Equal(1, StepsFromSourceToTarget(script, StepMode.Out)); - } + Assert.Equal(1, StepsFromSourceToTarget(script, StepMode.Out)); + } - [Fact] - public void StepsIntoSetAccessor() - { - var script = @" + [Fact] + public void StepsIntoSetAccessor() + { + var script = @" const obj = { set test(value) { @@ -259,13 +259,13 @@ set test(value) 'source'; obj.test = 37; // first step"; - Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Into)); - } + Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Into)); + } - [Fact] - public void StepsOverSetAccessor() - { - var script = @" + [Fact] + public void StepsOverSetAccessor() + { + var script = @" const obj = { set test(value) { @@ -276,13 +276,13 @@ set test(value) obj.test = 37; 'target';"; - Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Over)); - } + Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Over)); + } - [Fact] - public void StepsOutOfSetAccessor() - { - var script = @" + [Fact] + public void StepsOutOfSetAccessor() + { + var script = @" const obj = { set test(value) { @@ -294,26 +294,26 @@ set test(value) obj.test = 37; 'target';"; - Assert.Equal(1, StepsFromSourceToTarget(script, StepMode.Out)); - } + Assert.Equal(1, StepsFromSourceToTarget(script, StepMode.Out)); + } - [Fact] - public void ReturnPointIsAStep() - { - var script = @" + [Fact] + public void ReturnPointIsAStep() + { + var script = @" function test() { 'source'; } test(); 'target';"; - Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Over)); - } + Assert.Equal(2, StepsFromSourceToTarget(script, StepMode.Over)); + } - [Fact] - public void ReturnStatementIsAStep() - { - var script = @" + [Fact] + public void ReturnStatementIsAStep() + { + var script = @" function test() { 'source'; @@ -321,13 +321,13 @@ function test() } test(); 'target';"; - Assert.Equal(3, StepsFromSourceToTarget(script, StepMode.Over)); - } + Assert.Equal(3, StepsFromSourceToTarget(script, StepMode.Over)); + } - [Fact] - public void StepOutOnlyStepsOutOneStackLevel() - { - var script = @" + [Fact] + public void StepOutOnlyStepsOutOneStackLevel() + { + var script = @" function test() { 'dummy'; @@ -344,34 +344,34 @@ function test2() test();"; - var engine = new Engine(options => options.DebugMode()); - int step = 0; - engine.Debugger.Step += (sender, info) => + var engine = new Engine(options => options.DebugMode()); + int step = 0; + engine.Debugger.Step += (sender, info) => + { + switch (step) { - switch (step) - { - case 0: - if (info.ReachedLiteral("source")) - { - step++; - return StepMode.Out; - } - break; - case 1: - Assert.True(info.ReachedLiteral("target")); + case 0: + if (info.ReachedLiteral("source")) + { step++; - break; - } - return StepMode.Into; - }; + return StepMode.Out; + } + break; + case 1: + Assert.True(info.ReachedLiteral("target")); + step++; + break; + } + return StepMode.Into; + }; - engine.Execute(script); - } + engine.Execute(script); + } - [Fact] - public void StepOverDoesSinglestepAfterBreakpoint() - { - string script = @" + [Fact] + public void StepOverDoesSinglestepAfterBreakpoint() + { + string script = @" test(); function test() @@ -381,33 +381,33 @@ function test() 'target'; }"; - var engine = new Engine(options => options - .DebugMode() - .DebuggerStatementHandling(DebuggerStatementHandling.Script)); + var engine = new Engine(options => options + .DebugMode() + .DebuggerStatementHandling(DebuggerStatementHandling.Script)); - bool stepping = false; + bool stepping = false; - engine.Debugger.Break += (sender, info) => - { - stepping = true; - return StepMode.Over; - }; - engine.Debugger.Step += (sender, info) => + engine.Debugger.Break += (sender, info) => + { + stepping = true; + return StepMode.Over; + }; + engine.Debugger.Step += (sender, info) => + { + if (stepping) { - if (stepping) - { - Assert.True(info.ReachedLiteral("target")); - } - return StepMode.None; - }; + Assert.True(info.ReachedLiteral("target")); + } + return StepMode.None; + }; - engine.Execute(script); - } + engine.Execute(script); + } - [Fact] - public void StepNotTriggeredWhenRunning() - { - string script = @" + [Fact] + public void StepNotTriggeredWhenRunning() + { + string script = @" test(); function test() @@ -416,27 +416,27 @@ function test() 'dummy'; }"; - var engine = new Engine(options => options - .DebugMode() - .InitialStepMode(StepMode.Into)); + var engine = new Engine(options => options + .DebugMode() + .InitialStepMode(StepMode.Into)); - int stepCount = 0; - engine.Debugger.Step += (sender, info) => - { - stepCount++; - // Start running after first step - return StepMode.None; - }; + int stepCount = 0; + engine.Debugger.Step += (sender, info) => + { + stepCount++; + // Start running after first step + return StepMode.None; + }; - engine.Execute(script); + engine.Execute(script); - Assert.Equal(1, stepCount); - } + Assert.Equal(1, stepCount); + } - [Fact] - public void SkipIsTriggeredWhenRunning() - { - string script = @" + [Fact] + public void SkipIsTriggeredWhenRunning() + { + string script = @" 'step'; 'skip'; 'skip'; @@ -445,39 +445,38 @@ public void SkipIsTriggeredWhenRunning() 'step'; "; - var engine = new Engine(options => options - .DebugMode() - .DebuggerStatementHandling(DebuggerStatementHandling.Script) - .InitialStepMode(StepMode.Into)); + var engine = new Engine(options => options + .DebugMode() + .DebuggerStatementHandling(DebuggerStatementHandling.Script) + .InitialStepMode(StepMode.Into)); - int stepCount = 0; - int skipCount = 0; + int stepCount = 0; + int skipCount = 0; - engine.Debugger.Step += (sender, info) => - { - Assert.True(TestHelpers.IsLiteral(info.CurrentNode, "step")); - stepCount++; - // Start running after first step - return stepCount == 1 ? StepMode.None : StepMode.Into; - }; + engine.Debugger.Step += (sender, info) => + { + Assert.True(TestHelpers.IsLiteral(info.CurrentNode, "step")); + stepCount++; + // Start running after first step + return stepCount == 1 ? StepMode.None : StepMode.Into; + }; - engine.Debugger.Skip += (sender, info) => - { - Assert.True(TestHelpers.IsLiteral(info.CurrentNode, "skip")); - skipCount++; - return StepMode.None; - }; + engine.Debugger.Skip += (sender, info) => + { + Assert.True(TestHelpers.IsLiteral(info.CurrentNode, "skip")); + skipCount++; + return StepMode.None; + }; - engine.Debugger.Break += (sender, info) => - { - // Back to stepping after debugger statement - return StepMode.Into; - }; + engine.Debugger.Break += (sender, info) => + { + // Back to stepping after debugger statement + return StepMode.Into; + }; - engine.Execute(script); + engine.Execute(script); - Assert.Equal(2, skipCount); - Assert.Equal(3, stepCount); - } + Assert.Equal(2, skipCount); + Assert.Equal(3, stepCount); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Debugger/TestHelpers.cs b/Jint.Tests/Runtime/Debugger/TestHelpers.cs index ace2035cea..04e157a576 100644 --- a/Jint.Tests/Runtime/Debugger/TestHelpers.cs +++ b/Jint.Tests/Runtime/Debugger/TestHelpers.cs @@ -1,71 +1,70 @@ using Jint.Runtime.Debugger; -namespace Jint.Tests.Runtime.Debugger +namespace Jint.Tests.Runtime.Debugger; + +public static class TestHelpers { - public static class TestHelpers + public static bool IsLiteral(this Node node, string requiredValue = null) { - public static bool IsLiteral(this Node node, string requiredValue = null) + return node switch { - return node switch - { - Directive directive => requiredValue == null || directive.Value == requiredValue, - NonSpecialExpressionStatement expr => requiredValue == null || (expr.Expression is StringLiteral literal && literal.Value == requiredValue), - _ => false - }; - } + Directive directive => requiredValue == null || directive.Value == requiredValue, + NonSpecialExpressionStatement expr => requiredValue == null || (expr.Expression is StringLiteral literal && literal.Value == requiredValue), + _ => false + }; + } - public static bool ReachedLiteral(this DebugInformation info, string requiredValue) - { - return info.CurrentNode.IsLiteral(requiredValue); - } + public static bool ReachedLiteral(this DebugInformation info, string requiredValue) + { + return info.CurrentNode.IsLiteral(requiredValue); + } - /// - /// Initializes engine in debugmode and executes script until debugger statement, - /// before calling stepHandler for assertions. Also asserts that a break was triggered. - /// - /// Action to initialize and execute scripts - /// Handler for assertions - public static void TestAtBreak(Action initialization, Action breakHandler) - { - var engine = new Engine(options => options - .DebugMode() - .DebuggerStatementHandling(DebuggerStatementHandling.Script) - ); + /// + /// Initializes engine in debugmode and executes script until debugger statement, + /// before calling stepHandler for assertions. Also asserts that a break was triggered. + /// + /// Action to initialize and execute scripts + /// Handler for assertions + public static void TestAtBreak(Action initialization, Action breakHandler) + { + var engine = new Engine(options => options + .DebugMode() + .DebuggerStatementHandling(DebuggerStatementHandling.Script) + ); - bool didBreak = false; - engine.Debugger.Break += (sender, info) => - { - didBreak = true; - breakHandler(sender as Engine, info); - return StepMode.None; - }; + bool didBreak = false; + engine.Debugger.Break += (sender, info) => + { + didBreak = true; + breakHandler(sender as Engine, info); + return StepMode.None; + }; - initialization(engine); + initialization(engine); - Assert.True(didBreak, "Test script did not break (e.g. didn't reach debugger statement)"); - } + Assert.True(didBreak, "Test script did not break (e.g. didn't reach debugger statement)"); + } - /// - public static void TestAtBreak(Action initialization, Action breakHandler) - { - TestAtBreak(engine => initialization(engine), (engine, info) => breakHandler(info)); - } + /// + public static void TestAtBreak(Action initialization, Action breakHandler) + { + TestAtBreak(engine => initialization(engine), (engine, info) => breakHandler(info)); + } - /// - /// Initializes engine in debugmode and executes script until debugger statement, - /// before calling stepHandler for assertions. Also asserts that a break was triggered. - /// - /// Script that is basis for testing - /// Handler for assertions - public static void TestAtBreak(string script, Action breakHandler) - { - TestAtBreak(engine => engine.Execute(script), breakHandler); - } + /// + /// Initializes engine in debugmode and executes script until debugger statement, + /// before calling stepHandler for assertions. Also asserts that a break was triggered. + /// + /// Script that is basis for testing + /// Handler for assertions + public static void TestAtBreak(string script, Action breakHandler) + { + TestAtBreak(engine => engine.Execute(script), breakHandler); + } - /// - public static void TestAtBreak(string script, Action breakHandler) - { - TestAtBreak(script, (engine, info) => breakHandler(info)); - } + /// + public static void TestAtBreak(string script, Action breakHandler) + { + TestAtBreak(script, (engine, info) => breakHandler(info)); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/A.cs b/Jint.Tests/Runtime/Domain/A.cs index 9a167a54f6..b14d89c5bd 100644 --- a/Jint.Tests/Runtime/Domain/A.cs +++ b/Jint.Tests/Runtime/Domain/A.cs @@ -1,127 +1,126 @@ using Jint.Native; -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public class A { - public class A - { - public int Call1() - { - return 0; - } - - public int Call1(int x) - { - return x; - } - - public string Call2(string x) - { - return x; - } - - public string Call3(object x) - { - return x.ToString(); - } - - public string Call4(IPerson x) - { - return x.ToString(); - } - - public string Call5(Delegate callback) - { - var thisArg = JsValue.Undefined; - var arguments = new JsValue[] { 1, "foo" }; - - return callback.DynamicInvoke(thisArg, arguments).ToString(); - } - - public string Call6(Func callback) - { - var thisArg = new JsString("bar"); - var arguments = new JsValue[] { 1, "foo" }; - - return callback(thisArg, arguments).ToString(); - } - - public bool Call7(string str, Func predicate) - { - return predicate(str); - } - - public string Call8(Func predicate) - { - return predicate(); - } - - public void Call9(Action predicate) - { - predicate(); - } - - public void Call10(string str, Action predicate) - { - predicate(str); - } - - public void Call11(string str, string str2, Action predicate) - { - predicate(str, str2); - } - - public int Call12(int value, Func map) - { - return map(value); - } - - public string Call13(params object[] values) - { - return String.Join(",", values); - } - - public string Call14(string firstParam, params object[] values) - { - return String.Format("{0}:{1}", firstParam, String.Join(",", values)); - } - - public void Call15(string x) - { - - } - public string Call16(params JsValue[] values) - { - return String.Join(",", (System.Collections.Generic.IEnumerable)values); - } - - public int Call17(Func callback) - { - return callback(17); - } - - public void Call18(Action callback) - { - callback(18); - } - - public int Call19(int a = 0) - { - return a; - } - - public static int Call19Static(int a = 0) - { - return a; - } - - public int Call20(int a, int b = 1, int c = 2) - { - return a + b + c; - } - - public static int Call20Static(int a, int b = 1, int c = 2) - { - return a + b + c; - } - } -} + public int Call1() + { + return 0; + } + + public int Call1(int x) + { + return x; + } + + public string Call2(string x) + { + return x; + } + + public string Call3(object x) + { + return x.ToString(); + } + + public string Call4(IPerson x) + { + return x.ToString(); + } + + public string Call5(Delegate callback) + { + var thisArg = JsValue.Undefined; + var arguments = new JsValue[] { 1, "foo" }; + + return callback.DynamicInvoke(thisArg, arguments).ToString(); + } + + public string Call6(Func callback) + { + var thisArg = new JsString("bar"); + var arguments = new JsValue[] { 1, "foo" }; + + return callback(thisArg, arguments).ToString(); + } + + public bool Call7(string str, Func predicate) + { + return predicate(str); + } + + public string Call8(Func predicate) + { + return predicate(); + } + + public void Call9(Action predicate) + { + predicate(); + } + + public void Call10(string str, Action predicate) + { + predicate(str); + } + + public void Call11(string str, string str2, Action predicate) + { + predicate(str, str2); + } + + public int Call12(int value, Func map) + { + return map(value); + } + + public string Call13(params object[] values) + { + return String.Join(",", values); + } + + public string Call14(string firstParam, params object[] values) + { + return String.Format("{0}:{1}", firstParam, String.Join(",", values)); + } + + public void Call15(string x) + { + + } + public string Call16(params JsValue[] values) + { + return String.Join(",", (System.Collections.Generic.IEnumerable)values); + } + + public int Call17(Func callback) + { + return callback(17); + } + + public void Call18(Action callback) + { + callback(18); + } + + public int Call19(int a = 0) + { + return a; + } + + public static int Call19Static(int a = 0) + { + return a; + } + + public int Call20(int a, int b = 1, int c = 2) + { + return a + b + c; + } + + public static int Call20Static(int a, int b = 1, int c = 2) + { + return a + b + c; + } +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/ArrayConverterTestClass.cs b/Jint.Tests/Runtime/Domain/ArrayConverterTestClass.cs index 9209376caa..38df9c65e8 100644 --- a/Jint.Tests/Runtime/Domain/ArrayConverterTestClass.cs +++ b/Jint.Tests/Runtime/Domain/ArrayConverterTestClass.cs @@ -1,131 +1,130 @@ using System.Globalization; -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public class ArrayConverterTestClass { - public class ArrayConverterTestClass - { - public string MethodAcceptsArrayOfStrings(string[] arrayOfStrings) - { - return SerializeToString(arrayOfStrings); - } - - public string MethodAcceptsArrayOfInt(int[] arrayOfInt) - { - return SerializeToString(arrayOfInt); - } - - public string MethodAcceptsArrayOfBool(int[] arrayOfBool) - { - return SerializeToString(arrayOfBool); - } - - private static string SerializeToString(IEnumerable array) - { - return String.Join(",", array); - } - } - - public class ArrayConverterItem : IConvertible - { - private readonly int _value; - - public ArrayConverterItem(int value) - { - _value = value; - } - - public override string ToString() - { - return ToString(CultureInfo.InvariantCulture); - } - - public string ToString(IFormatProvider provider) - { - return _value.ToString(provider); - } - - public int ToInt32(IFormatProvider provider) - { - return Convert.ToInt32(_value, provider); - } - - #region NotImplemented - public TypeCode GetTypeCode() - { - return TypeCode.Object; - } - - public bool ToBoolean(IFormatProvider provider) - { - throw new NotImplementedException(); - } - - public char ToChar(IFormatProvider provider) - { - throw new NotImplementedException(); - } - - public sbyte ToSByte(IFormatProvider provider) - { - throw new NotImplementedException(); - } - - public byte ToByte(IFormatProvider provider) - { - throw new NotImplementedException(); - } - - public short ToInt16(IFormatProvider provider) - { - throw new NotImplementedException(); - } - - public ushort ToUInt16(IFormatProvider provider) - { - throw new NotImplementedException(); - } - - public uint ToUInt32(IFormatProvider provider) - { - throw new NotImplementedException(); - } - - public long ToInt64(IFormatProvider provider) - { - throw new NotImplementedException(); - } - - public ulong ToUInt64(IFormatProvider provider) - { - throw new NotImplementedException(); - } - - public float ToSingle(IFormatProvider provider) - { - throw new NotImplementedException(); - } - - public double ToDouble(IFormatProvider provider) - { - throw new NotImplementedException(); - } - - public decimal ToDecimal(IFormatProvider provider) - { - throw new NotImplementedException(); - } - - public DateTime ToDateTime(IFormatProvider provider) - { - throw new NotImplementedException(); - } - - public object ToType(Type conversionType, IFormatProvider provider) - { - throw new NotImplementedException(); - } - #endregion + public string MethodAcceptsArrayOfStrings(string[] arrayOfStrings) + { + return SerializeToString(arrayOfStrings); + } + public string MethodAcceptsArrayOfInt(int[] arrayOfInt) + { + return SerializeToString(arrayOfInt); + } + public string MethodAcceptsArrayOfBool(int[] arrayOfBool) + { + return SerializeToString(arrayOfBool); + } + + private static string SerializeToString(IEnumerable array) + { + return String.Join(",", array); } } + +public class ArrayConverterItem : IConvertible +{ + private readonly int _value; + + public ArrayConverterItem(int value) + { + _value = value; + } + + public override string ToString() + { + return ToString(CultureInfo.InvariantCulture); + } + + public string ToString(IFormatProvider provider) + { + return _value.ToString(provider); + } + + public int ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(_value, provider); + } + + #region NotImplemented + public TypeCode GetTypeCode() + { + return TypeCode.Object; + } + + public bool ToBoolean(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public char ToChar(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public sbyte ToSByte(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public byte ToByte(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public short ToInt16(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public ushort ToUInt16(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public uint ToUInt32(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public long ToInt64(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public ulong ToUInt64(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public float ToSingle(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public double ToDouble(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public decimal ToDecimal(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public DateTime ToDateTime(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public object ToType(Type conversionType, IFormatProvider provider) + { + throw new NotImplementedException(); + } + #endregion + + +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/ClassWithField.cs b/Jint.Tests/Runtime/Domain/ClassWithField.cs index 945a50e6fd..4c7008b1d2 100644 --- a/Jint.Tests/Runtime/Domain/ClassWithField.cs +++ b/Jint.Tests/Runtime/Domain/ClassWithField.cs @@ -1,7 +1,6 @@ -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public class ClassWithField { - public class ClassWithField - { - public string Field; - } -} + public string Field; +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/ClassWithStaticFields.cs b/Jint.Tests/Runtime/Domain/ClassWithStaticFields.cs index fb8e082c4e..145dbcc555 100644 --- a/Jint.Tests/Runtime/Domain/ClassWithStaticFields.cs +++ b/Jint.Tests/Runtime/Domain/ClassWithStaticFields.cs @@ -1,52 +1,51 @@ -namespace Jint.Tests.Runtime.Domain -{ - public class ClassWithStaticFields - { - public static string Get = "Get"; - public static string Set = "Set"; - - public static string Getter { get { return "Getter"; } } - public static string Setter { get; set; } - - public static readonly string Readonly = "Readonly"; - - static ClassWithStaticFields() - { - Setter = "Setter"; - } +namespace Jint.Tests.Runtime.Domain; + +public class ClassWithStaticFields +{ + public static string Get = "Get"; + public static string Set = "Set"; + + public static string Getter { get { return "Getter"; } } + public static string Setter { get; set; } + + public static readonly string Readonly = "Readonly"; + + static ClassWithStaticFields() + { + Setter = "Setter"; } +} - public class Nested +public class Nested +{ + public class ClassWithStaticFields { - public class ClassWithStaticFields + public static string Get = "Get"; + public static string Set = "Set"; + + public static string Getter { - public static string Get = "Get"; - public static string Set = "Set"; + get { return "Getter"; } + } - public static string Getter + public static string Setter + { + get { - get { return "Getter"; } + return _setter; } - - public static string Setter + set { - get - { - return _setter; - } - set - { - _setter = value; - } + _setter = value; } + } - public static readonly string Readonly = "Readonly"; - private static string _setter; + public static readonly string Readonly = "Readonly"; + private static string _setter; - static ClassWithStaticFields() - { - Setter = "Setter"; - } + static ClassWithStaticFields() + { + Setter = "Setter"; } } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/Colors.cs b/Jint.Tests/Runtime/Domain/Colors.cs index a64251af3d..f862e5363c 100644 --- a/Jint.Tests/Runtime/Domain/Colors.cs +++ b/Jint.Tests/Runtime/Domain/Colors.cs @@ -1,10 +1,9 @@ -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +[Flags] +public enum Colors { - [Flags] - public enum Colors - { - Red, - Green, - Blue = 10 - } -} + Red, + Green, + Blue = 10 +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/Company.cs b/Jint.Tests/Runtime/Domain/Company.cs index 39770347f6..192663bbda 100644 --- a/Jint.Tests/Runtime/Domain/Company.cs +++ b/Jint.Tests/Runtime/Domain/Company.cs @@ -1,44 +1,43 @@ -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public class Company : ICompany, IComparable { - public class Company : ICompany, IComparable - { - private string _name; + private string _name; - private readonly Dictionary _dictionary = new Dictionary() - { - {"key", "value"} - }; + private readonly Dictionary _dictionary = new Dictionary() + { + {"key", "value"} + }; - public Company(string name) - { - _name = name; - } + public Company(string name) + { + _name = name; + } - string ICompany.Name - { - get => _name; - set => _name = value; - } + string ICompany.Name + { + get => _name; + set => _name = value; + } - string ICompany.this[string key] - { - get => _dictionary[key]; - set => _dictionary[key] = value; - } + string ICompany.this[string key] + { + get => _dictionary[key]; + set => _dictionary[key] = value; + } - public string Item => "item thingie"; + public string Item => "item thingie"; - int IComparable.CompareTo(ICompany other) - { - return string.Compare(_name, other.Name, StringComparison.CurrentCulture); - } + int IComparable.CompareTo(ICompany other) + { + return string.Compare(_name, other.Name, StringComparison.CurrentCulture); + } - public IEnumerable GetNameChars() + public IEnumerable GetNameChars() + { + foreach (var c in _name) { - foreach (var c in _name) - { - yield return c; - } + yield return c; } } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/CustomNamed.cs b/Jint.Tests/Runtime/Domain/CustomNamed.cs index 9d0964b91b..05c2b7903c 100644 --- a/Jint.Tests/Runtime/Domain/CustomNamed.cs +++ b/Jint.Tests/Runtime/Domain/CustomNamed.cs @@ -1,65 +1,64 @@ -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] +public class CustomNameAttribute : Attribute { - [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] - public class CustomNameAttribute : Attribute + public CustomNameAttribute(string name) { - public CustomNameAttribute(string name) - { - Name = name; - } - - public string Name { get; } + Name = name; } - public interface ICustomNamed - { - [CustomName("jsInterfaceStringProperty")] - public string InterfaceStringProperty { get; } + public string Name { get; } +} - [CustomName("jsInterfaceMethod")] - public string InterfaceMethod(); - } +public interface ICustomNamed +{ + [CustomName("jsInterfaceStringProperty")] + public string InterfaceStringProperty { get; } - [CustomName("jsCustomName")] - public class CustomNamed : ICustomNamed - { - [CustomName("jsStringField")] - [CustomName("jsStringField2")] - public string StringField = "StringField"; + [CustomName("jsInterfaceMethod")] + public string InterfaceMethod(); +} - [CustomName("jsStaticStringField")] - public static string StaticStringField = "StaticStringField"; +[CustomName("jsCustomName")] +public class CustomNamed : ICustomNamed +{ + [CustomName("jsStringField")] + [CustomName("jsStringField2")] + public string StringField = "StringField"; - [CustomName("jsStringProperty")] - public string StringProperty => "StringProperty"; + [CustomName("jsStaticStringField")] + public static string StaticStringField = "StaticStringField"; - [CustomName("jsMethod")] - public string Method() => "Method"; + [CustomName("jsStringProperty")] + public string StringProperty => "StringProperty"; - [CustomName("jsStaticMethod")] - public static string StaticMethod() => "StaticMethod"; + [CustomName("jsMethod")] + public string Method() => "Method"; - public string InterfaceStringProperty => "InterfaceStringProperty"; + [CustomName("jsStaticMethod")] + public static string StaticMethod() => "StaticMethod"; - public string InterfaceMethod() => "InterfaceMethod"; + public string InterfaceStringProperty => "InterfaceStringProperty"; - [CustomName("jsEnumProperty")] - public CustomNamedEnum EnumProperty { get; set; } - } + public string InterfaceMethod() => "InterfaceMethod"; - [CustomName("XmlHttpRequest")] - public enum CustomNamedEnum - { - [CustomName("NONE")] - None = 0, + [CustomName("jsEnumProperty")] + public CustomNamedEnum EnumProperty { get; set; } +} - [CustomName("HEADERS_RECEIVED")] - HeadersReceived = 2 - } +[CustomName("XmlHttpRequest")] +public enum CustomNamedEnum +{ + [CustomName("NONE")] + None = 0, - public static class CustomNamedExtensions - { - [CustomName("jsExtensionMethod")] - public static string ExtensionMethod(this CustomNamed customNamed) => "ExtensionMethod"; - } + [CustomName("HEADERS_RECEIVED")] + HeadersReceived = 2 } + +public static class CustomNamedExtensions +{ + [CustomName("jsExtensionMethod")] + public static string ExtensionMethod(this CustomNamed customNamed) => "ExtensionMethod"; +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/Dimensional.cs b/Jint.Tests/Runtime/Domain/Dimensional.cs index 7e0069a253..dc05d3ebdc 100644 --- a/Jint.Tests/Runtime/Domain/Dimensional.cs +++ b/Jint.Tests/Runtime/Domain/Dimensional.cs @@ -1,115 +1,114 @@ -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public class Dimensional : IComparable, IEquatable { - public class Dimensional : IComparable, IEquatable - { - private readonly MeasureUnit[] PossibleMeasureUnits = new MeasureUnit[] { new MeasureUnit("Mass", "kg", 1.0), new MeasureUnit("Mass", "gr", 0.001), new MeasureUnit("Count", "piece", 1.0) }; + private readonly MeasureUnit[] PossibleMeasureUnits = new MeasureUnit[] { new MeasureUnit("Mass", "kg", 1.0), new MeasureUnit("Mass", "gr", 0.001), new MeasureUnit("Count", "piece", 1.0) }; - public MeasureUnit MeasureUnit { get; private set; } + public MeasureUnit MeasureUnit { get; private set; } - public double Value { get; private set; } + public double Value { get; private set; } - public double NormalizatedValue + public double NormalizatedValue + { + get { - get - { - return Value * MeasureUnit.RelativeValue; - } + return Value * MeasureUnit.RelativeValue; } + } - public Dimensional(string measureUnit, double value) - { - MeasureUnit = GetMeasureUnitByName(measureUnit); - Value = value; - } + public Dimensional(string measureUnit, double value) + { + MeasureUnit = GetMeasureUnitByName(measureUnit); + Value = value; + } - public static Dimensional operator +(Dimensional left, Dimensional right) - { - if (left.MeasureUnit.MeasureType != right.MeasureUnit.MeasureType) - throw new InvalidOperationException("Dimensionals with different measure types are non-summable"); + public static Dimensional operator +(Dimensional left, Dimensional right) + { + if (left.MeasureUnit.MeasureType != right.MeasureUnit.MeasureType) + throw new InvalidOperationException("Dimensionals with different measure types are non-summable"); - return new Dimensional(left.MeasureUnit.RelativeValue <= right.MeasureUnit.RelativeValue ? left.MeasureUnit.Name : right.MeasureUnit.Name, - left.Value * left.MeasureUnit.RelativeValue + right.Value * right.MeasureUnit.RelativeValue); - } + return new Dimensional(left.MeasureUnit.RelativeValue <= right.MeasureUnit.RelativeValue ? left.MeasureUnit.Name : right.MeasureUnit.Name, + left.Value * left.MeasureUnit.RelativeValue + right.Value * right.MeasureUnit.RelativeValue); + } - private MeasureUnit GetMeasureUnitByName(string name) - { - return PossibleMeasureUnits.FirstOrDefault(mu => mu.Name == name); - } + private MeasureUnit GetMeasureUnitByName(string name) + { + return PossibleMeasureUnits.FirstOrDefault(mu => mu.Name == name); + } - public int CompareTo(Dimensional obj) - { - if (MeasureUnit.MeasureType != obj.MeasureUnit.MeasureType) - throw new InvalidOperationException("Dimensionals with different measure types are non-comparable"); - return NormalizatedValue.CompareTo(obj.NormalizatedValue); - } + public int CompareTo(Dimensional obj) + { + if (MeasureUnit.MeasureType != obj.MeasureUnit.MeasureType) + throw new InvalidOperationException("Dimensionals with different measure types are non-comparable"); + return NormalizatedValue.CompareTo(obj.NormalizatedValue); + } - public override string ToString() - { - return Value + " " + MeasureUnit.Name; - } + public override string ToString() + { + return Value + " " + MeasureUnit.Name; + } - public bool Equals(Dimensional other) + public bool Equals(Dimensional other) + { + if (ReferenceEquals(null, other)) { - if (ReferenceEquals(null, other)) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return Value.Equals(other.Value) && Equals(MeasureUnit, other.MeasureUnit); + return false; } - public override bool Equals(object obj) + if (ReferenceEquals(this, other)) { - return Equals(obj as Dimensional); + return true; } - public override int GetHashCode() - { - return Value.GetHashCode(); - } + return Value.Equals(other.Value) && Equals(MeasureUnit, other.MeasureUnit); } - public class MeasureUnit : IEquatable + public override bool Equals(object obj) { - public string MeasureType { get; set; } - public string Name { get; set; } - public double RelativeValue { get; set; } - - public MeasureUnit(string measureType, string name, double relativeValue) - { - this.MeasureType = measureType; - this.Name = name; - this.RelativeValue = relativeValue; - } + return Equals(obj as Dimensional); + } - public bool Equals(MeasureUnit other) - { - if (ReferenceEquals(null, other)) - { - return false; - } + public override int GetHashCode() + { + return Value.GetHashCode(); + } +} - if (ReferenceEquals(this, other)) - { - return true; - } +public class MeasureUnit : IEquatable +{ + public string MeasureType { get; set; } + public string Name { get; set; } + public double RelativeValue { get; set; } - return MeasureType == other.MeasureType && Name == other.Name && RelativeValue.Equals(other.RelativeValue); - } + public MeasureUnit(string measureType, string name, double relativeValue) + { + this.MeasureType = measureType; + this.Name = name; + this.RelativeValue = relativeValue; + } - public override bool Equals(object obj) + public bool Equals(MeasureUnit other) + { + if (ReferenceEquals(null, other)) { - return Equals(obj as MeasureUnit); + return false; } - public override int GetHashCode() + if (ReferenceEquals(this, other)) { - return MeasureType.GetHashCode() ^ Name.GetHashCode() ^ RelativeValue.GetHashCode(); + return true; } + + return MeasureType == other.MeasureType && Name == other.Name && RelativeValue.Equals(other.RelativeValue); } -} + + public override bool Equals(object obj) + { + return Equals(obj as MeasureUnit); + } + + public override int GetHashCode() + { + return MeasureType.GetHashCode() ^ Name.GetHashCode() ^ RelativeValue.GetHashCode(); + } +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/Enums.cs b/Jint.Tests/Runtime/Domain/Enums.cs index 44770b25fc..80ff0c3a17 100644 --- a/Jint.Tests/Runtime/Domain/Enums.cs +++ b/Jint.Tests/Runtime/Domain/Enums.cs @@ -1,14 +1,13 @@ -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public enum IntegerEnum { - public enum IntegerEnum - { - a, - b - } + a, + b +} - public enum UintEnum : uint - { - a, - b - } +public enum UintEnum : uint +{ + a, + b } \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/FloatIndexer.cs b/Jint.Tests/Runtime/Domain/FloatIndexer.cs index 42500620fb..786d4d3b48 100644 --- a/Jint.Tests/Runtime/Domain/FloatIndexer.cs +++ b/Jint.Tests/Runtime/Domain/FloatIndexer.cs @@ -1,7 +1,6 @@ -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public class FloatIndexer { - public class FloatIndexer - { - public string this[int index] => ""; - } -} + public string this[int index] => ""; +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/HiddenMembers.cs b/Jint.Tests/Runtime/Domain/HiddenMembers.cs index 87f54df417..34a21c225d 100644 --- a/Jint.Tests/Runtime/Domain/HiddenMembers.cs +++ b/Jint.Tests/Runtime/Domain/HiddenMembers.cs @@ -1,22 +1,21 @@ -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public class HiddenMembers { - public class HiddenMembers - { - [Obsolete] - public string Field1 = "Field1"; + [Obsolete] + public string Field1 = "Field1"; - public string Field2 = "Field2"; + public string Field2 = "Field2"; - [Obsolete] - public string Member1 { get; set; } = "Member1"; + [Obsolete] + public string Member1 { get; set; } = "Member1"; - public string Member2 { get; set; } = "Member2"; + public string Member2 { get; set; } = "Member2"; - [Obsolete] - public string Method1() => "Method1"; + [Obsolete] + public string Method1() => "Method1"; - public string Method2() => "Method2"; + public string Method2() => "Method2"; - public Type Type => GetType(); - } -} + public Type Type => GetType(); +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/ICompany.cs b/Jint.Tests/Runtime/Domain/ICompany.cs index af874e1476..8940552971 100644 --- a/Jint.Tests/Runtime/Domain/ICompany.cs +++ b/Jint.Tests/Runtime/Domain/ICompany.cs @@ -1,8 +1,7 @@ -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public interface ICompany { - public interface ICompany - { - string Name { get; set; } - string this[string key] { get; set; } - } + string Name { get; set; } + string this[string key] { get; set; } } \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/IPerson.cs b/Jint.Tests/Runtime/Domain/IPerson.cs index 7a91d3f671..4e44527a18 100644 --- a/Jint.Tests/Runtime/Domain/IPerson.cs +++ b/Jint.Tests/Runtime/Domain/IPerson.cs @@ -1,8 +1,6 @@ -namespace Jint.Tests.Runtime.Domain -{ - public interface IPerson - { - string Name { get; } - } +namespace Jint.Tests.Runtime.Domain; -} +public interface IPerson +{ + string Name { get; } +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/IntegerIndexer.cs b/Jint.Tests/Runtime/Domain/IntegerIndexer.cs index 02b3c1b086..bdafd7c378 100644 --- a/Jint.Tests/Runtime/Domain/IntegerIndexer.cs +++ b/Jint.Tests/Runtime/Domain/IntegerIndexer.cs @@ -1,18 +1,17 @@ -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public class IntegerIndexer { - public class IntegerIndexer - { - private readonly int[] data; + private readonly int[] data; - public IntegerIndexer() - { - data = new[] {123, 0, 0, 0, 0}; - } + public IntegerIndexer() + { + data = new[] {123, 0, 0, 0, 0}; + } - public int this[int i] - { - get => data[i]; - set => data[i] = value; - } + public int this[int i] + { + get => data[i]; + set => data[i] = value; } } \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/JsUuid.cs b/Jint.Tests/Runtime/Domain/JsUuid.cs index 67ea364f30..2aedf8440d 100644 --- a/Jint.Tests/Runtime/Domain/JsUuid.cs +++ b/Jint.Tests/Runtime/Domain/JsUuid.cs @@ -1,26 +1,25 @@ using Jint.Native; -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public sealed class JsUuid : JsValue, IEquatable { - public sealed class JsUuid : JsValue, IEquatable - { - internal readonly Guid _value; - public static readonly JsUuid Empty = new JsUuid(Guid.Empty); + internal readonly Guid _value; + public static readonly JsUuid Empty = new JsUuid(Guid.Empty); - public JsUuid(Guid value) : base(Jint.Runtime.Types.String) => _value = value; + public JsUuid(Guid value) : base(Jint.Runtime.Types.String) => _value = value; - public static implicit operator JsUuid(Guid g) => new JsUuid(g); + public static implicit operator JsUuid(Guid g) => new JsUuid(g); - public override bool Equals(JsValue other) => Equals(other as JsUuid); + public override bool Equals(JsValue other) => Equals(other as JsUuid); - public bool Equals(JsUuid other) => other?._value == _value; + public bool Equals(JsUuid other) => other?._value == _value; - public override int GetHashCode() => _value.GetHashCode(); + public override int GetHashCode() => _value.GetHashCode(); - public override object ToObject() => _value; + public override object ToObject() => _value; - public override string ToString() => _value.ToString(); + public override string ToString() => _value.ToString(); - public override bool Equals(object obj) => Equals(obj as JsUuid); - } -} + public override bool Equals(object obj) => Equals(obj as JsUuid); +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/OverLoading.cs b/Jint.Tests/Runtime/Domain/OverLoading.cs index 70dbd016d9..a809901458 100644 --- a/Jint.Tests/Runtime/Domain/OverLoading.cs +++ b/Jint.Tests/Runtime/Domain/OverLoading.cs @@ -1,30 +1,29 @@ -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public class OverLoading { - public class OverLoading + public string TestFunc(string e) { - public string TestFunc(string e) - { - return "string"; - } + return "string"; + } - public string TestFunc(IntegerEnum e) - { - return "integer-enum"; - } + public string TestFunc(IntegerEnum e) + { + return "integer-enum"; + } - public string TestFunc(UintEnum e) - { - return "uint-enum"; - } + public string TestFunc(UintEnum e) + { + return "uint-enum"; + } - public string TestFunc(float f) - { - return "float-val"; - } + public string TestFunc(float f) + { + return "float-val"; + } - public string TestFunc(int i) - { - return "int-val"; - } + public string TestFunc(int i) + { + return "int-val"; } } \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/Person.cs b/Jint.Tests/Runtime/Domain/Person.cs index 7ea62f09c5..4ebe56a113 100644 --- a/Jint.Tests/Runtime/Domain/Person.cs +++ b/Jint.Tests/Runtime/Domain/Person.cs @@ -1,45 +1,44 @@ -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public class Person : IPerson { - public class Person : IPerson + public string Name { get; set; } + public int Age { get; set; } + + public Type TypeProperty { get; set; } = typeof(Person); + + public override string ToString() { - public string Name { get; set; } - public int Age { get; set; } + return Name; + } - public Type TypeProperty { get; set; } = typeof(Person); + protected bool Equals(Person other) + { + return Name == other.Name; + } - public override string ToString() + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { - return Name; + return false; } - protected bool Equals(Person other) + if (ReferenceEquals(this, obj)) { - return Name == other.Name; + return true; } - public override bool Equals(object obj) + if (obj.GetType() != GetType()) { - if (ReferenceEquals(null, obj)) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - return Equals((Person) obj); + return false; } - public override int GetHashCode() - { - return (Name != null ? Name.GetHashCode() : 0); - } + return Equals((Person) obj); + } + + public override int GetHashCode() + { + return (Name != null ? Name.GetHashCode() : 0); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/Thrower.cs b/Jint.Tests/Runtime/Domain/Thrower.cs index 07b110992b..fd53ee4984 100644 --- a/Jint.Tests/Runtime/Domain/Thrower.cs +++ b/Jint.Tests/Runtime/Domain/Thrower.cs @@ -1,25 +1,24 @@ -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public class Thrower { - public class Thrower + public void ThrowArgumentNullException() { - public void ThrowArgumentNullException() - { - throw new ArgumentNullException(); - } + throw new ArgumentNullException(); + } - public void ThrowExceptionWithMessage(string message) - { - throw new Exception(message); - } + public void ThrowExceptionWithMessage(string message) + { + throw new Exception(message); + } - public void ThrowNotSupportedException() - { - throw new NotSupportedException(); - } + public void ThrowNotSupportedException() + { + throw new NotSupportedException(); + } - public void ThrowNotSupportedExceptionWithMessage(string message) - { - throw new NotSupportedException(message); - } + public void ThrowNotSupportedExceptionWithMessage(string message) + { + throw new NotSupportedException(message); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/UuidConstructor.cs b/Jint.Tests/Runtime/Domain/UuidConstructor.cs index ead219e783..c05885535f 100644 --- a/Jint.Tests/Runtime/Domain/UuidConstructor.cs +++ b/Jint.Tests/Runtime/Domain/UuidConstructor.cs @@ -4,65 +4,64 @@ using Jint.Runtime.Descriptors; using Jint.Runtime.Interop; -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +internal sealed class UuidConstructor : Constructor { - internal sealed class UuidConstructor : Constructor - { - private static readonly JsString _functionName = new JsString("Uuid"); + private static readonly JsString _functionName = new JsString("Uuid"); - private UuidConstructor(Engine engine) : base(engine, engine.Realm, _functionName) - { - } + private UuidConstructor(Engine engine) : base(engine, engine.Realm, _functionName) + { + } - private JsValue Parse(JsValue @this, JsValue[] arguments) + private JsValue Parse(JsValue @this, JsValue[] arguments) + { + switch (arguments.At(0)) { - switch (arguments.At(0)) - { - case JsUuid uid: - return Construct(uid); + case JsUuid uid: + return Construct(uid); - case JsValue js when Guid.TryParse(js.AsString(), out var res): - return Construct(res); - } - - return Undefined; + case JsValue js when Guid.TryParse(js.AsString(), out var res): + return Construct(res); } - protected internal override ObjectInstance GetPrototypeOf() => _prototype; + return Undefined; + } + + protected internal override ObjectInstance GetPrototypeOf() => _prototype; - internal new ObjectInstance _prototype; + internal new ObjectInstance _prototype; - public UuidPrototype PrototypeObject { get; private set; } + public UuidPrototype PrototypeObject { get; private set; } - public static UuidConstructor CreateUuidConstructor(Engine engine) + public static UuidConstructor CreateUuidConstructor(Engine engine) + { + var obj = new UuidConstructor(engine) { - var obj = new UuidConstructor(engine) - { - // The value of the [[Prototype]] internal property of the Uuid constructor is the Function prototype object - _prototype = engine.Realm.Intrinsics.Function.PrototypeObject - }; - obj.PrototypeObject = UuidPrototype.CreatePrototypeObject(engine, obj); + // The value of the [[Prototype]] internal property of the Uuid constructor is the Function prototype object + _prototype = engine.Realm.Intrinsics.Function.PrototypeObject + }; + obj.PrototypeObject = UuidPrototype.CreatePrototypeObject(engine, obj); - // The initial value of Uuid.prototype is the Date prototype object - obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, false, false, false)); + // The initial value of Uuid.prototype is the Date prototype object + obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, false, false, false)); - engine.SetValue("Uuid", obj); - obj.Configure(); - obj.PrototypeObject.Configure(); + engine.SetValue("Uuid", obj); + obj.Configure(); + obj.PrototypeObject.Configure(); - return obj; - } + return obj; + } - protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments) => Construct(arguments, null); + protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments) => Construct(arguments, null); - public void Configure() - { - FastSetProperty("parse", new PropertyDescriptor(new ClrFunction(Engine, "parse", Parse), true, false, true)); - FastSetProperty("Empty", new PropertyDescriptor(JsUuid.Empty, true, false, true)); - } + public void Configure() + { + FastSetProperty("parse", new PropertyDescriptor(new ClrFunction(Engine, "parse", Parse), true, false, true)); + FastSetProperty("Empty", new PropertyDescriptor(JsUuid.Empty, true, false, true)); + } - public UuidInstance Construct(JsUuid uuid) => new UuidInstance(Engine) { PrimitiveValue = uuid, _prototype = PrototypeObject }; + public UuidInstance Construct(JsUuid uuid) => new UuidInstance(Engine) { PrimitiveValue = uuid, _prototype = PrototypeObject }; - public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget) => Construct(Guid.NewGuid()); - } -} + public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget) => Construct(Guid.NewGuid()); +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/UuidConverter.cs b/Jint.Tests/Runtime/Domain/UuidConverter.cs index e347a83440..9d716c2089 100644 --- a/Jint.Tests/Runtime/Domain/UuidConverter.cs +++ b/Jint.Tests/Runtime/Domain/UuidConverter.cs @@ -1,25 +1,24 @@ using Jint.Native; using Jint.Runtime.Interop; -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +public class UuidConverter : IObjectConverter { - public class UuidConverter : IObjectConverter + internal UuidConverter() { - internal UuidConverter() - { - } + } - public bool TryConvert(Engine engine, object value, out JsValue result) + public bool TryConvert(Engine engine, object value, out JsValue result) + { + switch (value) { - switch (value) - { - case Guid g: - result = new JsUuid(g); - return true; - } - - result = JsValue.Undefined; - return false; + case Guid g: + result = new JsUuid(g); + return true; } + + result = JsValue.Undefined; + return false; } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/UuidInstance.cs b/Jint.Tests/Runtime/Domain/UuidInstance.cs index c590248fd8..8027447000 100644 --- a/Jint.Tests/Runtime/Domain/UuidInstance.cs +++ b/Jint.Tests/Runtime/Domain/UuidInstance.cs @@ -1,20 +1,19 @@ using Jint.Native.Object; using Jint.Runtime.Interop; -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +internal class UuidInstance : ObjectInstance, IObjectWrapper { - internal class UuidInstance : ObjectInstance, IObjectWrapper - { - protected internal override ObjectInstance GetPrototypeOf() => _prototype; + protected internal override ObjectInstance GetPrototypeOf() => _prototype; - internal new ObjectInstance _prototype; + internal new ObjectInstance _prototype; - public JsUuid PrimitiveValue { get; set; } + public JsUuid PrimitiveValue { get; set; } - public object Target => PrimitiveValue?._value; + public object Target => PrimitiveValue?._value; - public UuidInstance(Engine engine) : base(engine) - { - } + public UuidInstance(Engine engine) : base(engine) + { } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/Domain/UuidPrototype.cs b/Jint.Tests/Runtime/Domain/UuidPrototype.cs index 7610350435..5e39724cba 100644 --- a/Jint.Tests/Runtime/Domain/UuidPrototype.cs +++ b/Jint.Tests/Runtime/Domain/UuidPrototype.cs @@ -3,40 +3,39 @@ using Jint.Runtime.Descriptors; using Jint.Runtime.Interop; -namespace Jint.Tests.Runtime.Domain +namespace Jint.Tests.Runtime.Domain; + +internal sealed class UuidPrototype : UuidInstance { - internal sealed class UuidPrototype : UuidInstance + private UuidPrototype(Engine engine) : base(engine) { - private UuidPrototype(Engine engine) : base(engine) - { - } + } - private UuidInstance EnsureUuidInstance(JsValue thisObject) - { - return thisObject.TryCast(value => throw new JavaScriptException(Engine.Realm.Intrinsics.TypeError, "Invalid Uuid")); - } + private UuidInstance EnsureUuidInstance(JsValue thisObject) + { + return thisObject.TryCast(value => throw new JavaScriptException(Engine.Realm.Intrinsics.TypeError, "Invalid Uuid")); + } - private JsValue ToGuidString(JsValue thisObject, JsValue[] arguments) => EnsureUuidInstance(thisObject).PrimitiveValue.ToString(); + private JsValue ToGuidString(JsValue thisObject, JsValue[] arguments) => EnsureUuidInstance(thisObject).PrimitiveValue.ToString(); - private JsValue ValueOf(JsValue thisObject, JsValue[] arguments) => EnsureUuidInstance(thisObject).PrimitiveValue; + private JsValue ValueOf(JsValue thisObject, JsValue[] arguments) => EnsureUuidInstance(thisObject).PrimitiveValue; - public static UuidPrototype CreatePrototypeObject(Engine engine, UuidConstructor ctor) + public static UuidPrototype CreatePrototypeObject(Engine engine, UuidConstructor ctor) + { + var obj = new UuidPrototype(engine) { - var obj = new UuidPrototype(engine) - { - PrimitiveValue = JsUuid.Empty, - _prototype = engine.Realm.Intrinsics.Object.PrototypeObject, - }; + PrimitiveValue = JsUuid.Empty, + _prototype = engine.Realm.Intrinsics.Object.PrototypeObject, + }; - obj.FastSetProperty("constructor", new PropertyDescriptor(ctor, false, false, true)); + obj.FastSetProperty("constructor", new PropertyDescriptor(ctor, false, false, true)); - return obj; - } + return obj; + } - public void Configure() - { - FastSetProperty("toString", new PropertyDescriptor(new ClrFunction(Engine, "toString", ToGuidString), true, false, true)); - FastSetProperty("valueOf", new PropertyDescriptor(new ClrFunction(Engine, "valueOf", ValueOf), true, false, true)); - } + public void Configure() + { + FastSetProperty("toString", new PropertyDescriptor(new ClrFunction(Engine, "toString", ToGuidString), true, false, true)); + FastSetProperty("valueOf", new PropertyDescriptor(new ClrFunction(Engine, "valueOf", ValueOf), true, false, true)); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/EngineTests.cs b/Jint.Tests/Runtime/EngineTests.cs index b403722d0c..7137610fec 100644 --- a/Jint.Tests/Runtime/EngineTests.cs +++ b/Jint.Tests/Runtime/EngineTests.cs @@ -11,136 +11,136 @@ #pragma warning disable 618 -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public partial class EngineTests : IDisposable { - public partial class EngineTests : IDisposable + private readonly Engine _engine; + private int countBreak = 0; + private StepMode stepMode; + private static readonly TimeZoneInfo _pacificTimeZone; + private static readonly TimeZoneInfo _tongaTimeZone; + private static readonly TimeZoneInfo _easternTimeZone; + + static EngineTests() { - private readonly Engine _engine; - private int countBreak = 0; - private StepMode stepMode; - private static readonly TimeZoneInfo _pacificTimeZone; - private static readonly TimeZoneInfo _tongaTimeZone; - private static readonly TimeZoneInfo _easternTimeZone; - - static EngineTests() + // https://stackoverflow.com/questions/47848111/how-should-i-fetch-timezoneinfo-in-a-platform-agnostic-way + // should be natively supported soon https://github.com/dotnet/runtime/issues/18644 + try { - // https://stackoverflow.com/questions/47848111/how-should-i-fetch-timezoneinfo-in-a-platform-agnostic-way - // should be natively supported soon https://github.com/dotnet/runtime/issues/18644 - try - { - _pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById("America/Los_Angeles"); - } - catch (TimeZoneNotFoundException) - { - _pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"); - } - - try - { - _tongaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific/Tongatapu"); - } - catch (TimeZoneNotFoundException) - { - _tongaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Tonga Standard Time"); - } + _pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById("America/Los_Angeles"); + } + catch (TimeZoneNotFoundException) + { + _pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"); + } - try - { - _easternTimeZone = TimeZoneInfo.FindSystemTimeZoneById("America/New_York"); - } - catch (TimeZoneNotFoundException) - { - _easternTimeZone = TimeZoneInfo.FindSystemTimeZoneById("US Eastern Standard Time"); - } + try + { + _tongaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific/Tongatapu"); + } + catch (TimeZoneNotFoundException) + { + _tongaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Tonga Standard Time"); } - public EngineTests(ITestOutputHelper output) + try + { + _easternTimeZone = TimeZoneInfo.FindSystemTimeZoneById("America/New_York"); + } + catch (TimeZoneNotFoundException) { - _engine = new Engine() + _easternTimeZone = TimeZoneInfo.FindSystemTimeZoneById("US Eastern Standard Time"); + } + } + + public EngineTests(ITestOutputHelper output) + { + _engine = new Engine() .SetValue("log", new Action(o => output.WriteLine(o.ToString()))) .SetValue("assert", new Action(Assert.True)) .SetValue("equal", new Action(Assert.Equal)) - ; - } + ; + } - void IDisposable.Dispose() - { - } + void IDisposable.Dispose() + { + } - private void RunTest(string source) - { - _engine.Execute(source); - } + private void RunTest(string source) + { + _engine.Execute(source); + } - internal static string GetEmbeddedFile(string filename) - { - const string Prefix = "Jint.Tests.Runtime.Scripts."; + internal static string GetEmbeddedFile(string filename) + { + const string Prefix = "Jint.Tests.Runtime.Scripts."; - var assembly = typeof(EngineTests).GetTypeInfo().Assembly; - var scriptPath = Prefix + filename; + var assembly = typeof(EngineTests).GetTypeInfo().Assembly; + var scriptPath = Prefix + filename; - using var stream = assembly.GetManifestResourceStream(scriptPath); - using var sr = new StreamReader(stream); - return sr.ReadToEnd(); - } + using var stream = assembly.GetManifestResourceStream(scriptPath); + using var sr = new StreamReader(stream); + return sr.ReadToEnd(); + } - [Theory] - [InlineData(42d, "42")] - [InlineData("Hello", "'Hello'")] - public void ShouldInterpretLiterals(object expected, string source) - { - var engine = new Engine(); - var result = engine.Evaluate(source).ToObject(); + [Theory] + [InlineData(42d, "42")] + [InlineData("Hello", "'Hello'")] + public void ShouldInterpretLiterals(object expected, string source) + { + var engine = new Engine(); + var result = engine.Evaluate(source).ToObject(); - Assert.Equal(expected, result); - } + Assert.Equal(expected, result); + } - [Fact] - public void ShouldInterpretVariableDeclaration() - { - var engine = new Engine(); - var result = engine - .Evaluate("var foo = 'bar'; foo;") - .ToObject(); + [Fact] + public void ShouldInterpretVariableDeclaration() + { + var engine = new Engine(); + var result = engine + .Evaluate("var foo = 'bar'; foo;") + .ToObject(); - Assert.Equal("bar", result); - } + Assert.Equal("bar", result); + } - [Theory] - [InlineData(4d, "1 + 3")] - [InlineData(-2d, "1 - 3")] - [InlineData(3d, "1 * 3")] - [InlineData(2d, "6 / 3")] - [InlineData(9d, "15 & 9")] - [InlineData(15d, "15 | 9")] - [InlineData(6d, "15 ^ 9")] - [InlineData(36d, "9 << 2")] - [InlineData(2d, "9 >> 2")] - [InlineData(4d, "19 >>> 2")] - public void ShouldInterpretBinaryExpression(object expected, string source) - { - var engine = new Engine(); - var result = engine.Evaluate(source).ToObject(); + [Theory] + [InlineData(4d, "1 + 3")] + [InlineData(-2d, "1 - 3")] + [InlineData(3d, "1 * 3")] + [InlineData(2d, "6 / 3")] + [InlineData(9d, "15 & 9")] + [InlineData(15d, "15 | 9")] + [InlineData(6d, "15 ^ 9")] + [InlineData(36d, "9 << 2")] + [InlineData(2d, "9 >> 2")] + [InlineData(4d, "19 >>> 2")] + public void ShouldInterpretBinaryExpression(object expected, string source) + { + var engine = new Engine(); + var result = engine.Evaluate(source).ToObject(); - Assert.Equal(expected, result); - } + Assert.Equal(expected, result); + } - [Theory] - [InlineData(-59d, "~58")] - [InlineData(58d, "~~58")] - public void ShouldInterpretUnaryExpression(object expected, string source) - { - var engine = new Engine(); - var result = engine.Evaluate(source).ToObject(); + [Theory] + [InlineData(-59d, "~58")] + [InlineData(58d, "~~58")] + public void ShouldInterpretUnaryExpression(object expected, string source) + { + var engine = new Engine(); + var result = engine.Evaluate(source).ToObject(); - Assert.Equal(expected, result); - } + Assert.Equal(expected, result); + } - [Fact] - public void ShouldHaveProperReferenceErrorMessage() - { - RunTest(@" + [Fact] + public void ShouldHaveProperReferenceErrorMessage() + { + RunTest(@" 'use strict'; var arr = [1, 2]; try { @@ -151,12 +151,12 @@ public void ShouldHaveProperReferenceErrorMessage() assert(ex.message === 'i is not defined'); } "); - } + } - [Fact] - public void ShouldHaveProperNotAFunctionErrorMessage() - { - RunTest(@" + [Fact] + public void ShouldHaveProperNotAFunctionErrorMessage() + { + RunTest(@" try { var example = {}; example(); @@ -166,51 +166,51 @@ public void ShouldHaveProperNotAFunctionErrorMessage() assert(ex.message === 'example is not a function'); } "); - } + } - [Fact] - public void ShouldEvaluateHasOwnProperty() - { - RunTest(@" + [Fact] + public void ShouldEvaluateHasOwnProperty() + { + RunTest(@" var x = {}; x.Bar = 42; assert(x.hasOwnProperty('Bar')); "); - } + } - [Fact] - public void ShouldAllowNullAsStringValue() - { - var engine = new Engine().SetValue("name", (string) null); - Assert.True(engine.Evaluate("name").IsNull()); - } + [Fact] + public void ShouldAllowNullAsStringValue() + { + var engine = new Engine().SetValue("name", (string) null); + Assert.True(engine.Evaluate("name").IsNull()); + } - [Fact] - public void FunctionConstructorsShouldCreateNewObjects() - { - RunTest(@" + [Fact] + public void FunctionConstructorsShouldCreateNewObjects() + { + RunTest(@" var Vehicle = function () {}; var vehicle = new Vehicle(); assert(vehicle != undefined); "); - } + } - [Fact] - public void NewObjectsInheritFunctionConstructorProperties() - { - RunTest(@" + [Fact] + public void NewObjectsInheritFunctionConstructorProperties() + { + RunTest(@" var Vehicle = function () {}; var vehicle = new Vehicle(); Vehicle.prototype.wheelCount = 4; assert(vehicle.wheelCount == 4); assert((new Vehicle()).wheelCount == 4); "); - } + } - [Fact] - public void PrototypeFunctionIsInherited() - { - RunTest(@" + [Fact] + public void PrototypeFunctionIsInherited() + { + RunTest(@" function Body(mass){ this.mass = mass; } @@ -225,13 +225,13 @@ function Body(mass){ assert(b.mass == 42); "); - } + } - [Fact] - public void FunctionConstructorCall() - { - RunTest(@" + [Fact] + public void FunctionConstructorCall() + { + RunTest(@" function Body(mass){ this.mass = mass; } @@ -239,12 +239,12 @@ function Body(mass){ var john = new Body(36); assert(john.mass == 36); "); - } + } - [Fact] - public void ArrowFunctionCall() - { - RunTest(@" + [Fact] + public void ArrowFunctionCall() + { + RunTest(@" var add = (a, b) => { return a + b; } @@ -252,23 +252,23 @@ public void ArrowFunctionCall() var x = add(1, 2); assert(x == 3); "); - } + } - [Fact] - public void ArrowFunctionExpressionCall() - { - RunTest(@" + [Fact] + public void ArrowFunctionExpressionCall() + { + RunTest(@" var add = (a, b) => a + b; var x = add(1, 2); assert(x === 3); "); - } + } - [Fact] - public void ArrowFunctionScope() - { - RunTest(@" + [Fact] + public void ArrowFunctionScope() + { + RunTest(@" var bob = { _name: ""Bob"", _friends: [""Alice""], @@ -278,84 +278,84 @@ public void ArrowFunctionScope() }; bob.printFriends(); "); - } + } - [Fact] - public void NewObjectsShouldUsePrivateProperties() - { - RunTest(@" + [Fact] + public void NewObjectsShouldUsePrivateProperties() + { + RunTest(@" var Vehicle = function (color) { this.color = color; }; var vehicle = new Vehicle('tan'); assert(vehicle.color == 'tan'); "); - } + } - [Fact] - public void FunctionConstructorsShouldDefinePrototypeChain() - { - RunTest(@" + [Fact] + public void FunctionConstructorsShouldDefinePrototypeChain() + { + RunTest(@" function Vehicle() {}; var vehicle = new Vehicle(); assert(vehicle.hasOwnProperty('constructor') == false); "); - } + } - [Fact] - public void NewObjectsConstructorIsObject() - { - RunTest(@" + [Fact] + public void NewObjectsConstructorIsObject() + { + RunTest(@" var o = new Object(); assert(o.constructor == Object); "); - } + } - [Fact] - public void NewObjectsIntanceOfConstructorObject() - { - RunTest(@" + [Fact] + public void NewObjectsIntanceOfConstructorObject() + { + RunTest(@" var o = new Object(); assert(o instanceof Object); "); - } + } - [Fact] - public void NewObjectsConstructorShouldBeConstructorObject() - { - RunTest(@" + [Fact] + public void NewObjectsConstructorShouldBeConstructorObject() + { + RunTest(@" var Vehicle = function () {}; var vehicle = new Vehicle(); assert(vehicle.constructor == Vehicle); "); - } + } - [Fact] - public void NewObjectsIntanceOfConstructorFunction() - { - RunTest(@" + [Fact] + public void NewObjectsIntanceOfConstructorFunction() + { + RunTest(@" var Vehicle = function () {}; var vehicle = new Vehicle(); assert(vehicle instanceof Vehicle); "); - } + } - [Fact] - public void ShouldEvaluateForLoops() - { - RunTest(@" + [Fact] + public void ShouldEvaluateForLoops() + { + RunTest(@" var foo = 0; for (var i = 0; i < 5; i++) { foo += i; } assert(foo == 10); "); - } + } - [Fact] - public void ShouldEvaluateRecursiveFunctions() - { - RunTest(@" + [Fact] + public void ShouldEvaluateRecursiveFunctions() + { + RunTest(@" function fib(n) { if (n < 2) { return n; @@ -365,143 +365,143 @@ function fib(n) { var result = fib(6); assert(result == 8); "); - } + } - [Fact] - public void ShouldAccessObjectProperties() - { - RunTest(@" + [Fact] + public void ShouldAccessObjectProperties() + { + RunTest(@" var o = {}; o.Foo = 'bar'; o.Baz = 42; o.Blah = o.Foo + o.Baz; assert(o.Blah == 'bar42'); "); - } + } - [Fact] - public void ShouldConstructArray() - { - RunTest(@" + [Fact] + public void ShouldConstructArray() + { + RunTest(@" var o = []; assert(o.length == 0); "); - } + } - [Fact] - public void ArrayPushShouldIncrementLength() - { - RunTest(@" + [Fact] + public void ArrayPushShouldIncrementLength() + { + RunTest(@" var o = []; o.push(1); assert(o.length == 1); "); - } + } - [Fact] - public void ArrayFunctionInitializesLength() - { - RunTest(@" + [Fact] + public void ArrayFunctionInitializesLength() + { + RunTest(@" assert(Array(3).length == 3); assert(Array('3').length == 1); "); - } + } - [Fact] - public void ArrayIndexerIsAssigned() - { - RunTest(@" + [Fact] + public void ArrayIndexerIsAssigned() + { + RunTest(@" var n = 8; var o = Array(n); for (var i = 0; i < n; i++) o[i] = i; equal(0, o[0]); equal(7, o[7]); "); - } + } - [Fact] - public void DenseArrayTurnsToSparseArrayWhenSizeGrowsTooMuch() - { - RunTest(@" + [Fact] + public void DenseArrayTurnsToSparseArrayWhenSizeGrowsTooMuch() + { + RunTest(@" var n = 1024*10+2; var o = Array(n); for (var i = 0; i < n; i++) o[i] = i; equal(0, o[0]); equal(n -1, o[n - 1]); "); - } + } - [Fact] - public void DenseArrayTurnsToSparseArrayWhenSparseIndexed() - { - RunTest(@" + [Fact] + public void DenseArrayTurnsToSparseArrayWhenSparseIndexed() + { + RunTest(@" var o = Array(); o[100] = 1; assert(o[100] == 1); "); - } + } - [Fact] - public void ArrayPopShouldDecrementLength() - { - RunTest(@" + [Fact] + public void ArrayPopShouldDecrementLength() + { + RunTest(@" var o = [42, 'foo']; var pop = o.pop(); assert(o.length == 1); assert(pop == 'foo'); "); - } + } - [Fact] - public void ArrayConstructor() - { - RunTest(@" + [Fact] + public void ArrayConstructor() + { + RunTest(@" var o = []; assert(o.constructor == Array); "); - } + } - [Fact] - public void DateConstructor() - { - RunTest(@" + [Fact] + public void DateConstructor() + { + RunTest(@" var o = new Date(); assert(o.constructor == Date); assert(o.hasOwnProperty('constructor') == false); "); - } + } - [Fact] - public void DateConstructorWithInvalidParameters() - { - RunTest(@" + [Fact] + public void DateConstructorWithInvalidParameters() + { + RunTest(@" var dt = new Date (1, Infinity); assert(isNaN(dt.getTime())); "); - } + } - [Fact] - public void ShouldConvertDateToNumber() - { - RunTest(@" + [Fact] + public void ShouldConvertDateToNumber() + { + RunTest(@" assert(Number(new Date(0)) === 0); "); - } + } - [Fact] - public void MathObjectIsDefined() - { - RunTest(@" + [Fact] + public void MathObjectIsDefined() + { + RunTest(@" var o = Math.abs(-1) assert(o == 1); "); - } + } - [Fact] - public void VoidShouldReturnUndefined() - { - RunTest(@" + [Fact] + public void VoidShouldReturnUndefined() + { + RunTest(@" assert(void 0 === undefined); var x = '1'; assert(void x === undefined); @@ -510,12 +510,12 @@ public void VoidShouldReturnUndefined() x = new String('-1'); assert (void x === undefined); "); - } + } - [Fact] - public void TypeofObjectShouldReturnString() - { - RunTest(@" + [Fact] + public void TypeofObjectShouldReturnString() + { + RunTest(@" assert(typeof x === 'undefined'); assert(typeof 0 === 'number'); var x = 0; @@ -523,56 +523,56 @@ public void TypeofObjectShouldReturnString() var x = new Object(); assert (typeof x === 'object'); "); - } + } - [Fact] - public void MathAbsReturnsAbsolute() - { - RunTest(@" + [Fact] + public void MathAbsReturnsAbsolute() + { + RunTest(@" assert(1 == Math.abs(-1)); "); - } + } - [Fact] - public void NaNIsNan() - { - RunTest(@" + [Fact] + public void NaNIsNan() + { + RunTest(@" var x = NaN; assert(isNaN(NaN)); assert(isNaN(Math.abs(x))); "); - } + } - [Theory] - [InlineData(2147483647, 1, 2147483648)] - [InlineData(-2147483647, -2, -2147483649)] - public void IntegerAdditionShouldNotOverflow(int lhs, int rhs, long result) - { - RunTest($"assert({lhs} + {rhs} == {result})"); - } + [Theory] + [InlineData(2147483647, 1, 2147483648)] + [InlineData(-2147483647, -2, -2147483649)] + public void IntegerAdditionShouldNotOverflow(int lhs, int rhs, long result) + { + RunTest($"assert({lhs} + {rhs} == {result})"); + } - [Theory] - [InlineData(2147483647, -1, 2147483648)] - [InlineData(-2147483647, 2, -2147483649)] - public void IntegerSubtractionShouldNotOverflow(int lhs, int rhs, long result) - { - RunTest($"assert({lhs} - {rhs} == {result})"); - } + [Theory] + [InlineData(2147483647, -1, 2147483648)] + [InlineData(-2147483647, 2, -2147483649)] + public void IntegerSubtractionShouldNotOverflow(int lhs, int rhs, long result) + { + RunTest($"assert({lhs} - {rhs} == {result})"); + } - [Fact] - public void ToNumberHandlesStringObject() - { - RunTest(@" + [Fact] + public void ToNumberHandlesStringObject() + { + RunTest(@" x = new String('1'); x *= undefined; assert(isNaN(x)); "); - } + } - [Fact] - public void FunctionScopesAreChained() - { - RunTest(@" + [Fact] + public void FunctionScopesAreChained() + { + RunTest(@" var x = 0; function f1(){ @@ -586,72 +586,72 @@ function f2(){ assert(f1() === undefined); "); - } + } - [Fact] - public void EvalFunctionParseAndExecuteCode() - { - RunTest(@" + [Fact] + public void EvalFunctionParseAndExecuteCode() + { + RunTest(@" var x = 0; eval('assert(x == 0)'); "); - } + } - [Fact] - public void ForInStatement() - { - var engine = new Engine(); - var result = engine.Evaluate(""" - var x, y, str = ''; - for(var z in this) { - str += z; - } - return str; - """); - Assert.Equal("xystrz", result); - } + [Fact] + public void ForInStatement() + { + var engine = new Engine(); + var result = engine.Evaluate(""" + var x, y, str = ''; + for(var z in this) { + str += z; + } + return str; + """); + Assert.Equal("xystrz", result); + } - [Fact] - public void ForInStatementEnumeratesKeys() - { - RunTest(@" + [Fact] + public void ForInStatementEnumeratesKeys() + { + RunTest(@" for(var i in 'abc'); log(i); assert(i === '2'); "); - } + } - [Fact] - public void WithStatement() - { - RunTest(@" + [Fact] + public void WithStatement() + { + RunTest(@" with (Math) { assert(cos(0) == 1); } "); - } + } - [Fact] - public void ObjectExpression() - { - RunTest(@" + [Fact] + public void ObjectExpression() + { + RunTest(@" var o = { x: 1 }; assert(o.x == 1); "); - } + } - [Fact] - public void StringFunctionCreatesString() - { - RunTest(@" + [Fact] + public void StringFunctionCreatesString() + { + RunTest(@" assert(String(NaN) === 'NaN'); "); - } + } - [Fact] - public void ScopeChainInWithStatement() - { - RunTest(@" + [Fact] + public void ScopeChainInWithStatement() + { + RunTest(@" var x = 0; var myObj = {x : 'obj'}; @@ -667,12 +667,12 @@ function f2(){ assert(f1() === 'obj'); "); - } + } - [Fact] - public void TryCatchBlockStatement() - { - RunTest(@" + [Fact] + public void TryCatchBlockStatement() + { + RunTest(@" var x, y, z; try { x = 1; @@ -693,85 +693,85 @@ public void TryCatchBlockStatement() assert(y == 1); assert(z == 1); "); - } + } - [Fact] - public void FunctionsCanBeAssigned() - { - RunTest(@" + [Fact] + public void FunctionsCanBeAssigned() + { + RunTest(@" var sin = Math.sin; assert(sin(0) == 0); "); - } + } - [Fact] - public void FunctionArgumentsIsDefined() - { - RunTest(@" + [Fact] + public void FunctionArgumentsIsDefined() + { + RunTest(@" function f() { assert(arguments.length > 0); } f(42); "); - } + } - [Fact] - public void PrimitiveValueFunctions() - { - RunTest(@" + [Fact] + public void PrimitiveValueFunctions() + { + RunTest(@" var s = (1).toString(); assert(s == '1'); "); - } + } - [Theory] - [InlineData(true, "'ab' == 'a' + 'b'")] - public void OperatorsPrecedence(object expected, string source) - { - var engine = new Engine(); - var result = engine.Evaluate(source).ToObject(); + [Theory] + [InlineData(true, "'ab' == 'a' + 'b'")] + public void OperatorsPrecedence(object expected, string source) + { + var engine = new Engine(); + var result = engine.Evaluate(source).ToObject(); - Assert.Equal(expected, result); - } + Assert.Equal(expected, result); + } - [Fact] - public void FunctionPrototypeShouldHaveApplyMethod() - { - RunTest(@" + [Fact] + public void FunctionPrototypeShouldHaveApplyMethod() + { + RunTest(@" var numbers = [5, 6, 2, 3, 7]; var max = Math.max.apply(null, numbers); assert(max == 7); "); - } + } - [Theory] - [InlineData(double.NaN, "parseInt(NaN)")] - [InlineData(double.NaN, "parseInt(null)")] - [InlineData(double.NaN, "parseInt(undefined)")] - [InlineData(double.NaN, "parseInt(new Boolean(true))")] - [InlineData(double.NaN, "parseInt(Infinity)")] - [InlineData(-1d, "parseInt(-1)")] - [InlineData(-1d, "parseInt('-1')")] - [InlineData(double.NaN, "parseInt(new Array(100000).join('Z'))")] - public void ShouldEvaluateParseInt(object expected, string source) - { - var engine = new Engine(); - var result = engine.Evaluate(source).ToObject(); + [Theory] + [InlineData(double.NaN, "parseInt(NaN)")] + [InlineData(double.NaN, "parseInt(null)")] + [InlineData(double.NaN, "parseInt(undefined)")] + [InlineData(double.NaN, "parseInt(new Boolean(true))")] + [InlineData(double.NaN, "parseInt(Infinity)")] + [InlineData(-1d, "parseInt(-1)")] + [InlineData(-1d, "parseInt('-1')")] + [InlineData(double.NaN, "parseInt(new Array(100000).join('Z'))")] + public void ShouldEvaluateParseInt(object expected, string source) + { + var engine = new Engine(); + var result = engine.Evaluate(source).ToObject(); - Assert.Equal(expected, result); - } + Assert.Equal(expected, result); + } - [Fact] - public void ShouldNotExecuteDebuggerStatement() - { - new Engine().Evaluate("debugger"); - } + [Fact] + public void ShouldNotExecuteDebuggerStatement() + { + new Engine().Evaluate("debugger"); + } - [Fact] - public void ShouldConvertDoubleToStringWithoutLosingPrecision() - { - RunTest(@" + [Fact] + public void ShouldConvertDoubleToStringWithoutLosingPrecision() + { + RunTest(@" assert(String(14.915832707045631) === '14.915832707045631'); assert(String(-14.915832707045631) === '-14.915832707045631'); assert(String(0.5) === '0.5'); @@ -781,12 +781,12 @@ public void ShouldConvertDoubleToStringWithoutLosingPrecision() assert(String(30.0) === '30'); assert(String(0.2388906159889881) === '0.2388906159889881'); "); - } + } - [Fact] - public void ShouldWriteNumbersUsingBases() - { - RunTest(@" + [Fact] + public void ShouldWriteNumbersUsingBases() + { + RunTest(@" assert(15.0.toString() === '15'); assert(15.0.toString(2) === '1111'); assert(15.0.toString(8) === '17'); @@ -795,151 +795,151 @@ public void ShouldWriteNumbersUsingBases() assert(15.0.toString(36) === 'f'); assert(15.1.toString(36) === 'f.3llllllllkau6snqkpygsc3di'); "); - } + } - [Fact] - public void ShouldNotAlterSlashesInRegex() - { - RunTest(@" + [Fact] + public void ShouldNotAlterSlashesInRegex() + { + RunTest(@" equal('/\\//', new RegExp('/').toString()); "); - } + } - [Fact] - public void ShouldHandleEscapedSlashesInRegex() - { - RunTest(@" + [Fact] + public void ShouldHandleEscapedSlashesInRegex() + { + RunTest(@" var regex = /[a-z]\/[a-z]/; assert(regex.test('a/b') === true); assert(regex.test('a\\/b') === false); "); - } + } - [Fact] - public void ShouldComputeFractionInBase() - { - Assert.Equal("011", NumberPrototype.ToFractionBase(0.375, 2)); - Assert.Equal("14141414141414141414141414141414141414141414141414", NumberPrototype.ToFractionBase(0.375, 5)); - } + [Fact] + public void ShouldComputeFractionInBase() + { + Assert.Equal("011", NumberPrototype.ToFractionBase(0.375, 2)); + Assert.Equal("14141414141414141414141414141414141414141414141414", NumberPrototype.ToFractionBase(0.375, 5)); + } - [Fact] - public void ShouldInvokeAFunctionValue() - { - RunTest(@" + [Fact] + public void ShouldInvokeAFunctionValue() + { + RunTest(@" function add(x, y) { return x + y; } "); - var add = _engine.GetValue("add"); + var add = _engine.GetValue("add"); - Assert.Equal(3, _engine.Invoke(add, 1, 2)); - } + Assert.Equal(3, _engine.Invoke(add, 1, 2)); + } - [Fact] - public void ShouldAllowInvokeAFunctionValueWithNullValueAsArgument() - { - RunTest(@" + [Fact] + public void ShouldAllowInvokeAFunctionValueWithNullValueAsArgument() + { + RunTest(@" function get(x) { return x; } "); - var add = _engine.GetValue("get"); - string str = null; - Assert.Equal(Native.JsValue.Null, _engine.Invoke(add, str)); - } + var add = _engine.GetValue("get"); + string str = null; + Assert.Equal(Native.JsValue.Null, _engine.Invoke(add, str)); + } - [Fact] - public void ShouldNotInvokeNonFunctionValue() - { - RunTest(@" + [Fact] + public void ShouldNotInvokeNonFunctionValue() + { + RunTest(@" var x= 10; "); - var x = _engine.GetValue("x"); + var x = _engine.GetValue("x"); - var exception = Assert.Throws(() => _engine.Invoke(x, 1, 2)); - Assert.Equal("Can only invoke functions", exception.Message); - } + var exception = Assert.Throws(() => _engine.Invoke(x, 1, 2)); + Assert.Equal("Can only invoke functions", exception.Message); + } - [Fact] - public void ShouldInvokeAFunctionValueThatBelongsToAnObject() - { - RunTest(@" + [Fact] + public void ShouldInvokeAFunctionValueThatBelongsToAnObject() + { + RunTest(@" var obj = { foo: 5, getFoo: function (bar) { return 'foo is ' + this.foo + ', bar is ' + bar; } }; "); - var obj = _engine.GetValue("obj").AsObject(); - var getFoo = obj.Get("getFoo"); + var obj = _engine.GetValue("obj").AsObject(); + var getFoo = obj.Get("getFoo"); - Assert.Equal("foo is 5, bar is 7", _engine.Invoke(getFoo, obj, new object[] { 7 }).AsString()); - } + Assert.Equal("foo is 5, bar is 7", _engine.Invoke(getFoo, obj, new object[] { 7 }).AsString()); + } - [Fact] - public void ShouldNotInvokeNonFunctionValueThatBelongsToAnObject() - { - RunTest(@" + [Fact] + public void ShouldNotInvokeNonFunctionValueThatBelongsToAnObject() + { + RunTest(@" var obj = { foo: 2 }; "); - var obj = _engine.GetValue("obj").AsObject(); - var foo = obj.Get("foo"); + var obj = _engine.GetValue("obj").AsObject(); + var foo = obj.Get("foo"); - Assert.Throws(() => _engine.Invoke(foo, obj, new object[] { })); - } + Assert.Throws(() => _engine.Invoke(foo, obj, new object[] { })); + } - [Fact] - public void ShouldNotAllowModifyingSharedUndefinedDescriptor() - { - var e = new Engine(); - e.Evaluate("var x = { literal: true };"); + [Fact] + public void ShouldNotAllowModifyingSharedUndefinedDescriptor() + { + var e = new Engine(); + e.Evaluate("var x = { literal: true };"); - var pd = e.GetValue("x").AsObject().GetOwnProperty("doesNotExist"); - Assert.Throws(() => pd.Value = "oh no, assigning this breaks things"); - } + var pd = e.GetValue("x").AsObject().GetOwnProperty("doesNotExist"); + Assert.Throws(() => pd.Value = "oh no, assigning this breaks things"); + } - [Theory] - [InlineData("0", 0, 16)] - [InlineData("1", 1, 16)] - [InlineData("100", 100, 10)] - [InlineData("1100100", 100, 2)] - [InlineData("2s", 100, 36)] - [InlineData("2qgpckvng1s", 10000000000000000L, 36)] - public void ShouldConvertNumbersToDifferentBase(string expected, long number, int radix) - { - var result = NumberPrototype.ToBase(number, radix); - Assert.Equal(expected, result); - } + [Theory] + [InlineData("0", 0, 16)] + [InlineData("1", 1, 16)] + [InlineData("100", 100, 10)] + [InlineData("1100100", 100, 2)] + [InlineData("2s", 100, 36)] + [InlineData("2qgpckvng1s", 10000000000000000L, 36)] + public void ShouldConvertNumbersToDifferentBase(string expected, long number, int radix) + { + var result = NumberPrototype.ToBase(number, radix); + Assert.Equal(expected, result); + } - [Fact] - public void JsonParserShouldParseNegativeNumber() - { - RunTest(@" + [Fact] + public void JsonParserShouldParseNegativeNumber() + { + RunTest(@" var a = JSON.parse('{ ""x"":-1 }'); assert(a.x === -1); var b = JSON.parse('{ ""x"": -1 }'); assert(b.x === -1); "); - } + } - [Fact] - public void JsonParserShouldUseToString() - { - RunTest(@" + [Fact] + public void JsonParserShouldUseToString() + { + RunTest(@" var a = JSON.parse(null); // Equivalent to JSON.parse('null') assert(a === null); "); - RunTest(@" + RunTest(@" var a = JSON.parse(true); // Equivalent to JSON.parse('true') assert(a === true); "); - RunTest(@" + RunTest(@" var a = JSON.parse(false); // Equivalent to JSON.parse('false') assert(a === false); "); - RunTest(@" + RunTest(@" try { JSON.parse(undefined); // Equivalent to JSON.parse('undefined') assert(false); @@ -949,7 +949,7 @@ public void JsonParserShouldUseToString() } "); - RunTest(@" + RunTest(@" try { JSON.parse({}); // Equivalent to JSON.parse('[object Object]') assert(false); @@ -959,7 +959,7 @@ public void JsonParserShouldUseToString() } "); - RunTest(@" + RunTest(@" try { JSON.parse(function() { }); // Equivalent to JSON.parse('function () {}') assert(false); @@ -968,12 +968,12 @@ public void JsonParserShouldUseToString() assert(ex instanceof SyntaxError); } "); - } + } - [Fact] - public void JsonParserShouldDetectInvalidNegativeNumberSyntax() - { - RunTest(@" + [Fact] + public void JsonParserShouldDetectInvalidNegativeNumberSyntax() + { + RunTest(@" try { JSON.parse('{ ""x"": -.1 }'); // Not allowed assert(false); @@ -983,7 +983,7 @@ public void JsonParserShouldDetectInvalidNegativeNumberSyntax() } "); - RunTest(@" + RunTest(@" try { JSON.parse('{ ""x"": - 1 }'); // Not allowed assert(false); @@ -992,19 +992,19 @@ public void JsonParserShouldDetectInvalidNegativeNumberSyntax() assert(ex instanceof SyntaxError); } "); - } + } - [Fact] - public void JsonParserShouldUseReviverFunction() - { - RunTest(@" + [Fact] + public void JsonParserShouldUseReviverFunction() + { + RunTest(@" var jsonObj = JSON.parse('{""p"": 5}', function (key, value){ return typeof value === 'number' ? value * 2 : value; }); assert(jsonObj.p === 10); "); - RunTest(@" + RunTest(@" var expectedKeys = [""1"", ""2"", ""4"", ""6"", ""5"", ""3"", """"]; var actualKeys = []; JSON.parse('{""1"": 1, ""2"": 2, ""3"": {""4"": 4, ""5"": {""6"": 6}}}', function (key, value){ @@ -1015,237 +1015,237 @@ public void JsonParserShouldUseReviverFunction() assert(actualKeys[i] === val); }); "); - } + } - [Fact] - public void JsonParserShouldHandleEmptyString() - { - var ex = Assert.Throws(() => _engine.Evaluate("JSON.parse('');")); - Assert.Equal("Unexpected end of JSON input at position 0", ex.Message); - } + [Fact] + public void JsonParserShouldHandleEmptyString() + { + var ex = Assert.Throws(() => _engine.Evaluate("JSON.parse('');")); + Assert.Equal("Unexpected end of JSON input at position 0", ex.Message); + } - [Fact] - [ReplaceCulture("fr-FR")] - public void ShouldBeCultureInvariant() - { - // decimals in french are separated by commas - var engine = new Engine(); + [Fact] + [ReplaceCulture("fr-FR")] + public void ShouldBeCultureInvariant() + { + // decimals in french are separated by commas + var engine = new Engine(); - var result = engine.Evaluate("1.2 + 2.1").AsNumber(); - Assert.Equal(3.3d, result); + var result = engine.Evaluate("1.2 + 2.1").AsNumber(); + Assert.Equal(3.3d, result); - result = engine.Evaluate("JSON.parse('{\"x\" : 3.3}').x").AsNumber(); - Assert.Equal(3.3d, result); - } + result = engine.Evaluate("JSON.parse('{\"x\" : 3.3}').x").AsNumber(); + Assert.Equal(3.3d, result); + } - [Fact] - public void ShouldGetParseErrorLocation() + [Fact] + public void ShouldGetParseErrorLocation() + { + var engine = new Engine(); + try { - var engine = new Engine(); - try - { - engine.Evaluate("1.2+ new", "jQuery.js"); - } - catch (JavaScriptException e) - { - Assert.Equal(1, e.Location.Start.Line); - Assert.Equal(8, e.Location.Start.Column); - Assert.Equal("jQuery.js", e.Location.SourceFile); - } + engine.Evaluate("1.2+ new", "jQuery.js"); } - #region DateParsingAndStrings - [Fact] - public void ParseShouldReturnNumber() + catch (JavaScriptException e) { - var engine = new Engine(); - - var result = engine.Evaluate("Date.parse('1970-01-01');").AsNumber(); - Assert.Equal(0, result); + Assert.Equal(1, e.Location.Start.Line); + Assert.Equal(8, e.Location.Start.Column); + Assert.Equal("jQuery.js", e.Location.SourceFile); } + } + #region DateParsingAndStrings + [Fact] + public void ParseShouldReturnNumber() + { + var engine = new Engine(); - [Fact] - public void TimeWithinDayShouldHandleNegativeValues() - { - RunTest(@" + var result = engine.Evaluate("Date.parse('1970-01-01');").AsNumber(); + Assert.Equal(0, result); + } + + [Fact] + public void TimeWithinDayShouldHandleNegativeValues() + { + RunTest(@" // using a date < 1970 so that the primitive value is negative var d = new Date(1958, 0, 1); d.setMonth(-1); assert(d.getDate() == 1); "); - } + } - [Fact] - public void LocalDateTimeShouldNotLoseTimezone() - { - var date = new DateTime(2016, 1, 1, 13, 0, 0, DateTimeKind.Local); - var engine = new Engine().SetValue("localDate", date); - var actual = engine.Evaluate(@"localDate").AsDate().ToDateTime(); - Assert.Equal(date.ToUniversalTime(), actual.ToUniversalTime()); - Assert.Equal(date.ToLocalTime(), actual.ToLocalTime()); - } + [Fact] + public void LocalDateTimeShouldNotLoseTimezone() + { + var date = new DateTime(2016, 1, 1, 13, 0, 0, DateTimeKind.Local); + var engine = new Engine().SetValue("localDate", date); + var actual = engine.Evaluate(@"localDate").AsDate().ToDateTime(); + Assert.Equal(date.ToUniversalTime(), actual.ToUniversalTime()); + Assert.Equal(date.ToLocalTime(), actual.ToLocalTime()); + } - [Fact] - public void UtcShouldUseUtc() - { - var customTimeZone = _tongaTimeZone; + [Fact] + public void UtcShouldUseUtc() + { + var customTimeZone = _tongaTimeZone; - var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone)); + var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone)); - var result = engine.Evaluate("Date.UTC(1970,0,1)").AsNumber(); - Assert.Equal(0, result); - } + var result = engine.Evaluate("Date.UTC(1970,0,1)").AsNumber(); + Assert.Equal(0, result); + } - [Fact] - public void ShouldUseLocalTimeZoneOverride() - { - const string customName = "Custom Time"; - var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(0, 11, 0), customName, customName, customName, null, false); + [Fact] + public void ShouldUseLocalTimeZoneOverride() + { + const string customName = "Custom Time"; + var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(0, 11, 0), customName, customName, customName, null, false); - var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone)); + var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone)); - var epochGetLocalMinutes = engine.Evaluate("var d = new Date(0); d.getMinutes();").AsNumber(); - Assert.Equal(11, epochGetLocalMinutes); + var epochGetLocalMinutes = engine.Evaluate("var d = new Date(0); d.getMinutes();").AsNumber(); + Assert.Equal(11, epochGetLocalMinutes); - var localEpochGetUtcMinutes = engine.Evaluate("var d = new Date(1970,0,1); d.getUTCMinutes();").AsNumber(); - Assert.Equal(49, localEpochGetUtcMinutes); + var localEpochGetUtcMinutes = engine.Evaluate("var d = new Date(1970,0,1); d.getUTCMinutes();").AsNumber(); + Assert.Equal(49, localEpochGetUtcMinutes); - var parseLocalEpoch = engine.Evaluate("Date.parse('January 1, 1970');").AsNumber(); - Assert.Equal(-11 * 60 * 1000, parseLocalEpoch); + var parseLocalEpoch = engine.Evaluate("Date.parse('January 1, 1970');").AsNumber(); + Assert.Equal(-11 * 60 * 1000, parseLocalEpoch); - var epochToLocalString = engine.Evaluate("var d = new Date(0); d.toString();").AsString(); - Assert.Equal("Thu Jan 01 1970 00:11:00 GMT+0011 (Custom Time)", epochToLocalString); + var epochToLocalString = engine.Evaluate("var d = new Date(0); d.toString();").AsString(); + Assert.Equal("Thu Jan 01 1970 00:11:00 GMT+0011 (Custom Time)", epochToLocalString); - var epochToUTCString = engine.Evaluate("var d = new Date(0); d.toUTCString();").AsString(); - Assert.Equal("Thu, 01 Jan 1970 00:00:00 GMT", epochToUTCString); - } + var epochToUTCString = engine.Evaluate("var d = new Date(0); d.toUTCString();").AsString(); + Assert.Equal("Thu, 01 Jan 1970 00:00:00 GMT", epochToUTCString); + } - [Theory] - [InlineData("1970")] - [InlineData("1970-01")] - [InlineData("1970-01-01")] - [InlineData("1970-01-01T00:00Z")] - [InlineData("1970-01-01T00:00:00Z")] - [InlineData("1970-01-01T00:00:00.000Z")] - [InlineData("1970Z")] - [InlineData("1970-1Z")] - [InlineData("1970-1-1Z")] - [InlineData("1970-1-1T0:0Z")] - [InlineData("1970-1-1T0:0:0Z")] - [InlineData("1970-1-1T0:0:0.0Z")] - [InlineData("1970/1Z")] - [InlineData("1970/1/1Z")] - [InlineData("1970/1/1 0:0Z")] - [InlineData("1970/1/1 0:0:0Z")] - [InlineData("1970/1/1 0:0:0.0Z")] - [InlineData("January 1, 1970 GMT")] - [InlineData("1970-01-01T00:00:00.000-00:00")] - public void ShouldParseAsUtc(string date) - { - var customTimeZone = _tongaTimeZone; - var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone)); + [Theory] + [InlineData("1970")] + [InlineData("1970-01")] + [InlineData("1970-01-01")] + [InlineData("1970-01-01T00:00Z")] + [InlineData("1970-01-01T00:00:00Z")] + [InlineData("1970-01-01T00:00:00.000Z")] + [InlineData("1970Z")] + [InlineData("1970-1Z")] + [InlineData("1970-1-1Z")] + [InlineData("1970-1-1T0:0Z")] + [InlineData("1970-1-1T0:0:0Z")] + [InlineData("1970-1-1T0:0:0.0Z")] + [InlineData("1970/1Z")] + [InlineData("1970/1/1Z")] + [InlineData("1970/1/1 0:0Z")] + [InlineData("1970/1/1 0:0:0Z")] + [InlineData("1970/1/1 0:0:0.0Z")] + [InlineData("January 1, 1970 GMT")] + [InlineData("1970-01-01T00:00:00.000-00:00")] + public void ShouldParseAsUtc(string date) + { + var customTimeZone = _tongaTimeZone; + var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone)); - engine.SetValue("d", date); - var result = engine.Evaluate("Date.parse(d);").AsNumber(); + engine.SetValue("d", date); + var result = engine.Evaluate("Date.parse(d);").AsNumber(); - Assert.Equal(0, result); - } + Assert.Equal(0, result); + } - [Theory] - [InlineData("1970-01-01T00:00")] - [InlineData("1970-01-01T00:00:00")] - [InlineData("1970-01-01T00:00:00.000")] - [InlineData("1970/01")] - [InlineData("1970/01/01")] - [InlineData("1970/01/01T00:00")] - [InlineData("1970/01/01 00:00")] - [InlineData("1970-1")] - [InlineData("1970-1-1")] - [InlineData("1970-1-1T0:0")] - [InlineData("1970-1-1 0:0")] - [InlineData("1970/1")] - [InlineData("1970/1/1")] - [InlineData("1970/1/1T0:0")] - [InlineData("1970/1/1 0:0")] - [InlineData("01-1970")] - [InlineData("01-01-1970")] - [InlineData("January 1, 1970")] - [InlineData("1970-01-01T00:00:00.000+00:11")] - public void ShouldParseAsLocalTime(string date) - { - const int timespanMinutes = 11; - const int msPriorMidnight = -timespanMinutes * 60 * 1000; - const string customName = "Custom Time"; - var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(0, timespanMinutes, 0), customName, customName, customName, null, false); - var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone)).SetValue("d", date); + [Theory] + [InlineData("1970-01-01T00:00")] + [InlineData("1970-01-01T00:00:00")] + [InlineData("1970-01-01T00:00:00.000")] + [InlineData("1970/01")] + [InlineData("1970/01/01")] + [InlineData("1970/01/01T00:00")] + [InlineData("1970/01/01 00:00")] + [InlineData("1970-1")] + [InlineData("1970-1-1")] + [InlineData("1970-1-1T0:0")] + [InlineData("1970-1-1 0:0")] + [InlineData("1970/1")] + [InlineData("1970/1/1")] + [InlineData("1970/1/1T0:0")] + [InlineData("1970/1/1 0:0")] + [InlineData("01-1970")] + [InlineData("01-01-1970")] + [InlineData("January 1, 1970")] + [InlineData("1970-01-01T00:00:00.000+00:11")] + public void ShouldParseAsLocalTime(string date) + { + const int timespanMinutes = 11; + const int msPriorMidnight = -timespanMinutes * 60 * 1000; + const string customName = "Custom Time"; + var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(0, timespanMinutes, 0), customName, customName, customName, null, false); + var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone)).SetValue("d", date); - var result = engine.Evaluate("Date.parse(d);").AsNumber(); + var result = engine.Evaluate("Date.parse(d);").AsNumber(); - Assert.Equal(msPriorMidnight, result); - } + Assert.Equal(msPriorMidnight, result); + } - public static System.Collections.Generic.IEnumerable TestDates + public static System.Collections.Generic.IEnumerable TestDates + { + get { - get - { - yield return new object[] { new DateTime(2000, 1, 1) }; - yield return new object[] { new DateTime(2000, 1, 1, 0, 15, 15, 15) }; - yield return new object[] { new DateTime(2000, 6, 1, 0, 15, 15, 15) }; - yield return new object[] { new DateTime(1900, 1, 1) }; - yield return new object[] { new DateTime(1900, 1, 1, 0, 15, 15, 15) }; - yield return new object[] { new DateTime(1900, 6, 1, 0, 15, 15, 15) }; - } + yield return new object[] { new DateTime(2000, 1, 1) }; + yield return new object[] { new DateTime(2000, 1, 1, 0, 15, 15, 15) }; + yield return new object[] { new DateTime(2000, 6, 1, 0, 15, 15, 15) }; + yield return new object[] { new DateTime(1900, 1, 1) }; + yield return new object[] { new DateTime(1900, 1, 1, 0, 15, 15, 15) }; + yield return new object[] { new DateTime(1900, 6, 1, 0, 15, 15, 15) }; } + } - [Theory, MemberData("TestDates")] - public void TestDateToISOStringFormat(DateTime testDate) - { - var customTimeZone = _pacificTimeZone; + [Theory, MemberData("TestDates")] + public void TestDateToISOStringFormat(DateTime testDate) + { + var customTimeZone = _pacificTimeZone; - var engine = new Engine(ctx => ctx.LocalTimeZone(customTimeZone)); - var testDateTimeOffset = new DateTimeOffset(testDate, customTimeZone.GetUtcOffset(testDate)); - engine.Execute( - string.Format("var d = new Date({0},{1},{2},{3},{4},{5},{6});", testDateTimeOffset.Year, testDateTimeOffset.Month - 1, testDateTimeOffset.Day, testDateTimeOffset.Hour, testDateTimeOffset.Minute, testDateTimeOffset.Second, testDateTimeOffset.Millisecond)); - Assert.Equal(testDateTimeOffset.UtcDateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'", CultureInfo.InvariantCulture), engine.Evaluate("d.toISOString();").ToString()); - } + var engine = new Engine(ctx => ctx.LocalTimeZone(customTimeZone)); + var testDateTimeOffset = new DateTimeOffset(testDate, customTimeZone.GetUtcOffset(testDate)); + engine.Execute( + string.Format("var d = new Date({0},{1},{2},{3},{4},{5},{6});", testDateTimeOffset.Year, testDateTimeOffset.Month - 1, testDateTimeOffset.Day, testDateTimeOffset.Hour, testDateTimeOffset.Minute, testDateTimeOffset.Second, testDateTimeOffset.Millisecond)); + Assert.Equal(testDateTimeOffset.UtcDateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'", CultureInfo.InvariantCulture), engine.Evaluate("d.toISOString();").ToString()); + } - [Theory, MemberData(nameof(TestDates))] - public void TestDateToStringFormat(DateTime testDate) - { - var customTimeZone = _pacificTimeZone; + [Theory, MemberData(nameof(TestDates))] + public void TestDateToStringFormat(DateTime testDate) + { + var customTimeZone = _pacificTimeZone; - var engine = new Engine(ctx => ctx.LocalTimeZone(customTimeZone)); - var dt = new DateTimeOffset(testDate, customTimeZone.GetUtcOffset(testDate)); - var dateScript = $"var d = new Date({dt.Year}, {dt.Month - 1}, {dt.Day}, {dt.Hour}, {dt.Minute}, {dt.Second}, {dt.Millisecond});"; - engine.Execute(dateScript); + var engine = new Engine(ctx => ctx.LocalTimeZone(customTimeZone)); + var dt = new DateTimeOffset(testDate, customTimeZone.GetUtcOffset(testDate)); + var dateScript = $"var d = new Date({dt.Year}, {dt.Month - 1}, {dt.Day}, {dt.Hour}, {dt.Minute}, {dt.Second}, {dt.Millisecond});"; + engine.Execute(dateScript); - var expected = dt.ToString("ddd MMM dd yyyy HH:mm:ss", CultureInfo.InvariantCulture); - expected += dt.ToString(" 'GMT'zzz", CultureInfo.InvariantCulture).Replace(":", ""); - expected += " (Pacific Standard Time)"; - var actual = engine.Evaluate("d.toString();").ToString(); + var expected = dt.ToString("ddd MMM dd yyyy HH:mm:ss", CultureInfo.InvariantCulture); + expected += dt.ToString(" 'GMT'zzz", CultureInfo.InvariantCulture).Replace(":", ""); + expected += " (Pacific Standard Time)"; + var actual = engine.Evaluate("d.toString();").ToString(); - Assert.Equal(expected, actual); - } + Assert.Equal(expected, actual); + } - #endregion + #endregion - //DateParsingAndStrings - [Fact] - public void EmptyStringShouldMatchRegex() - { - RunTest(@" + //DateParsingAndStrings + [Fact] + public void EmptyStringShouldMatchRegex() + { + RunTest(@" var regex = /^(?:$)/g; assert(''.match(regex) instanceof Array); "); - } + } - [Fact] - public void ShouldExecuteHandlebars() - { - var content = GetEmbeddedFile("handlebars.js"); + [Fact] + public void ShouldExecuteHandlebars() + { + var content = GetEmbeddedFile("handlebars.js"); - RunTest(content); + RunTest(content); - RunTest(@" + RunTest(@" var source = 'Hello {{name}}'; var template = Handlebars.compile(source); var context = {name: 'Paul'}; @@ -1253,16 +1253,16 @@ public void ShouldExecuteHandlebars() assert('Hello Paul' == html); "); - } + } - [Fact] - public void ShouldExecutePrism() - { - var content = GetEmbeddedFile("prism.js"); + [Fact] + public void ShouldExecutePrism() + { + var content = GetEmbeddedFile("prism.js"); - RunTest(content); + RunTest(content); - RunTest(@" + RunTest(@" var input = 'using System; public class Person { public int Name { get; set; } }'; var lang = 'csharp'; var highlighted = Prism.highlight(input, Prism.languages.csharp, lang); @@ -1274,125 +1274,125 @@ public void ShouldExecutePrism() log(highlighted); "); - _engine.SetValue("input", File.ReadAllText("../../../../Jint/Engine.cs")); - RunTest("Prism.highlight(input, Prism.languages.csharp, lang);"); - } + _engine.SetValue("input", File.ReadAllText("../../../../Jint/Engine.cs")); + RunTest("Prism.highlight(input, Prism.languages.csharp, lang);"); + } - [Fact] - public void ShouldExecuteDromaeoBase64() - { - RunTest(@" + [Fact] + public void ShouldExecuteDromaeoBase64() + { + RunTest(@" var startTest = function () { }; var test = function (name, fn) { fn(); }; var endTest = function () { }; var prep = function (fn) { fn(); }; "); - var content = GetEmbeddedFile("dromaeo-string-base64.js"); - RunTest(content); - } + var content = GetEmbeddedFile("dromaeo-string-base64.js"); + RunTest(content); + } - [Fact] - public void ShouldExecuteKnockoutWithoutErrorWhetherTolerantOrIntolerant() - { - var content = GetEmbeddedFile("knockout-3.4.0.js"); - _engine.Execute(content, new ScriptParsingOptions { Tolerant = true }); - _engine.Execute(content, new ScriptParsingOptions { Tolerant = false }); - } + [Fact] + public void ShouldExecuteKnockoutWithoutErrorWhetherTolerantOrIntolerant() + { + var content = GetEmbeddedFile("knockout-3.4.0.js"); + _engine.Execute(content, new ScriptParsingOptions { Tolerant = true }); + _engine.Execute(content, new ScriptParsingOptions { Tolerant = false }); + } - [Fact] - public void ShouldAllowProtoProperty() - { - var code = "if({ __proto__: [] } instanceof Array) {}"; - _engine.Execute(code); - _engine.Execute($"eval('{code}')"); - _engine.Execute($"new Function('{code}')"); - } + [Fact] + public void ShouldAllowProtoProperty() + { + var code = "if({ __proto__: [] } instanceof Array) {}"; + _engine.Execute(code); + _engine.Execute($"eval('{code}')"); + _engine.Execute($"new Function('{code}')"); + } - [Fact] - public void ShouldNotAllowDuplicateProtoProperty() - { - var code = "if({ __proto__: [], __proto__:[] } instanceof Array) {}"; + [Fact] + public void ShouldNotAllowDuplicateProtoProperty() + { + var code = "if({ __proto__: [], __proto__:[] } instanceof Array) {}"; - Exception ex = Assert.Throws(() => _engine.Execute(code, new ScriptParsingOptions { Tolerant = false })); - Assert.Contains("Duplicate __proto__ fields are not allowed in object literals", ex.Message); + Exception ex = Assert.Throws(() => _engine.Execute(code, new ScriptParsingOptions { Tolerant = false })); + Assert.Contains("Duplicate __proto__ fields are not allowed in object literals", ex.Message); - ex = Assert.Throws(() => _engine.Execute($"eval('{code}')")); - Assert.Contains("Duplicate __proto__ fields are not allowed in object literals", ex.Message); + ex = Assert.Throws(() => _engine.Execute($"eval('{code}')")); + Assert.Contains("Duplicate __proto__ fields are not allowed in object literals", ex.Message); - Assert.Throws(() => _engine.Execute($"new Function('{code}')")); - Assert.Contains("Duplicate __proto__ fields are not allowed in object literals", ex.Message); - } + Assert.Throws(() => _engine.Execute($"new Function('{code}')")); + Assert.Contains("Duplicate __proto__ fields are not allowed in object literals", ex.Message); + } - [Fact] - public void ShouldExecuteLodash() - { - var content = GetEmbeddedFile("lodash.min.js"); + [Fact] + public void ShouldExecuteLodash() + { + var content = GetEmbeddedFile("lodash.min.js"); - RunTest(content); - } + RunTest(content); + } - [Fact] - public void DateParseReturnsNaN() - { - RunTest(@" + [Fact] + public void DateParseReturnsNaN() + { + RunTest(@" var d = Date.parse('not a date'); assert(isNaN(d)); "); - } + } - [Fact] - public void ShouldIgnoreHtmlComments() - { - RunTest(@" + [Fact] + public void ShouldIgnoreHtmlComments() + { + RunTest(@" var d = Date.parse('not a date'); assert(isNaN(d)); "); - } + } - [Fact] - public void DateShouldAllowEntireDotNetDateRange() - { - var engine = new Engine(); + [Fact] + public void DateShouldAllowEntireDotNetDateRange() + { + var engine = new Engine(); - var minValue = engine.Evaluate("new Date('0001-01-01T00:00:00.000Z')").ToObject(); - Assert.Equal(new DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc), minValue); + var minValue = engine.Evaluate("new Date('0001-01-01T00:00:00.000Z')").ToObject(); + Assert.Equal(new DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc), minValue); - var maxValue = engine.Evaluate("new Date('9999-12-31T23:59:59.999Z')").ToObject(); + var maxValue = engine.Evaluate("new Date('9999-12-31T23:59:59.999Z')").ToObject(); #if NETCOREAPP Assert.Equal(new DateTime(9999, 12, 31, 23, 59, 59, 998, DateTimeKind.Utc), maxValue); #else - Assert.Equal(new DateTime(9999, 12, 31, 23, 59, 59, 999, DateTimeKind.Utc), maxValue); + Assert.Equal(new DateTime(9999, 12, 31, 23, 59, 59, 999, DateTimeKind.Utc), maxValue); #endif - } + } - [Fact] - public void ShouldConstructNewArrayWithInteger() - { - RunTest(@" + [Fact] + public void ShouldConstructNewArrayWithInteger() + { + RunTest(@" var a = new Array(3); assert(a.length === 3); assert(a[0] == undefined); assert(a[1] == undefined); assert(a[2] == undefined); "); - } + } - [Fact] - public void ShouldConstructNewArrayWithString() - { - RunTest(@" + [Fact] + public void ShouldConstructNewArrayWithString() + { + RunTest(@" var a = new Array('foo'); assert(a.length === 1); assert(a[0] === 'foo'); "); - } + } - [Fact] - public void ShouldThrowRangeExceptionWhenConstructedWithNonInteger() - { - RunTest(@" + [Fact] + public void ShouldThrowRangeExceptionWhenConstructedWithNonInteger() + { + RunTest(@" var result = false; try { var a = new Array(3.4); @@ -1403,74 +1403,74 @@ public void ShouldThrowRangeExceptionWhenConstructedWithNonInteger() assert(result); "); - } + } - [Fact] - public void ShouldInitializeArrayWithSingleIngegerValue() - { - RunTest(@" + [Fact] + public void ShouldInitializeArrayWithSingleIngegerValue() + { + RunTest(@" var a = [3]; assert(a.length === 1); assert(a[0] === 3); "); - } + } - [Fact] - public void ShouldInitializeJsonObjectArrayWithSingleIntegerValue() - { - RunTest(@" + [Fact] + public void ShouldInitializeJsonObjectArrayWithSingleIntegerValue() + { + RunTest(@" var x = JSON.parse('{ ""a"": [3] }'); assert(x.a.length === 1); assert(x.a[0] === 3); "); - } + } - [Fact] - public void ShouldInitializeJsonArrayWithSingleIntegerValue() - { - RunTest(@" + [Fact] + public void ShouldInitializeJsonArrayWithSingleIntegerValue() + { + RunTest(@" var a = JSON.parse('[3]'); assert(a.length === 1); assert(a[0] === 3); "); - } + } - [Fact] - public void ShouldReturnTrueForEmptyIsNaNStatement() - { - RunTest(@" + [Fact] + public void ShouldReturnTrueForEmptyIsNaNStatement() + { + RunTest(@" assert(true === isNaN()); "); - } + } - [Theory] - [InlineData(4d, 0, "4")] - [InlineData(4d, 1, "4.0")] - [InlineData(4d, 2, "4.00")] - [InlineData(28.995, 2, "29.00")] - [InlineData(-28.995, 2, "-29.00")] - [InlineData(-28.495, 2, "-28.50")] - [InlineData(-28.445, 2, "-28.45")] - [InlineData(28.445, 2, "28.45")] - [InlineData(10.995, 0, "11")] - public void ShouldRoundToFixedDecimal(double number, int fractionDigits, string result) - { - var engine = new Engine(); - var value = engine.Evaluate( + [Theory] + [InlineData(4d, 0, "4")] + [InlineData(4d, 1, "4.0")] + [InlineData(4d, 2, "4.00")] + [InlineData(28.995, 2, "29.00")] + [InlineData(-28.995, 2, "-29.00")] + [InlineData(-28.495, 2, "-28.50")] + [InlineData(-28.445, 2, "-28.45")] + [InlineData(28.445, 2, "28.45")] + [InlineData(10.995, 0, "11")] + public void ShouldRoundToFixedDecimal(double number, int fractionDigits, string result) + { + var engine = new Engine(); + var value = engine.Evaluate( String.Format("new Number({0}).toFixed({1})", number.ToString(CultureInfo.InvariantCulture), fractionDigits.ToString(CultureInfo.InvariantCulture))) - .ToObject(); + .ToObject(); - Assert.Equal(value, result); - } + Assert.Equal(value, result); + } - [Fact] - public void ShouldSortArrayWhenCompareFunctionReturnsFloatingPointNumber() - { - RunTest(@" + [Fact] + public void ShouldSortArrayWhenCompareFunctionReturnsFloatingPointNumber() + { + RunTest(@" var nums = [1, 1.1, 1.2, 2, 2, 2.1, 2.2]; nums.sort(function(a,b){return b-a;}); assert(nums[0] === 2.2); @@ -1481,88 +1481,88 @@ public void ShouldSortArrayWhenCompareFunctionReturnsFloatingPointNumber() assert(nums[5] === 1.1); assert(nums[6] === 1); "); - } + } - [Fact] - public void ShouldBreakWhenBreakpointIsReached() - { - countBreak = 0; - stepMode = StepMode.None; + [Fact] + public void ShouldBreakWhenBreakpointIsReached() + { + countBreak = 0; + stepMode = StepMode.None; - var engine = new Engine(options => options.DebugMode()); + var engine = new Engine(options => options.DebugMode()); - engine.Debugger.Break += EngineStep; + engine.Debugger.Break += EngineStep; - engine.Debugger.BreakPoints.Set(new BreakPoint(1, 0)); + engine.Debugger.BreakPoints.Set(new BreakPoint(1, 0)); - engine.Evaluate(@"var local = true; + engine.Evaluate(@"var local = true; if (local === true) {}"); - engine.Debugger.Break -= EngineStep; + engine.Debugger.Break -= EngineStep; - Assert.Equal(1, countBreak); - } + Assert.Equal(1, countBreak); + } - [Fact] - public void ShouldExecuteStepByStep() - { - countBreak = 0; - stepMode = StepMode.Into; + [Fact] + public void ShouldExecuteStepByStep() + { + countBreak = 0; + stepMode = StepMode.Into; - var engine = new Engine(options => options.DebugMode().InitialStepMode(stepMode)); + var engine = new Engine(options => options.DebugMode().InitialStepMode(stepMode)); - engine.Debugger.Step += EngineStep; + engine.Debugger.Step += EngineStep; - engine.Evaluate(@"var local = true; + engine.Evaluate(@"var local = true; var creatingSomeOtherLine = 0; var lastOneIPromise = true"); - engine.Debugger.Step -= EngineStep; + engine.Debugger.Step -= EngineStep; - Assert.Equal(3, countBreak); - } + Assert.Equal(3, countBreak); + } - [Fact] - public void ShouldNotBreakTwiceIfSteppingOverBreakpoint() - { - countBreak = 0; - stepMode = StepMode.Into; + [Fact] + public void ShouldNotBreakTwiceIfSteppingOverBreakpoint() + { + countBreak = 0; + stepMode = StepMode.Into; - var engine = new Engine(options => options.DebugMode().InitialStepMode(stepMode)); - engine.Debugger.BreakPoints.Set(new BreakPoint(1, 1)); - engine.Debugger.Step += EngineStep; - engine.Debugger.Break += EngineStep; + var engine = new Engine(options => options.DebugMode().InitialStepMode(stepMode)); + engine.Debugger.BreakPoints.Set(new BreakPoint(1, 1)); + engine.Debugger.Step += EngineStep; + engine.Debugger.Break += EngineStep; - engine.Evaluate(@"var local = true;"); + engine.Evaluate(@"var local = true;"); - engine.Debugger.Step -= EngineStep; - engine.Debugger.Break -= EngineStep; + engine.Debugger.Step -= EngineStep; + engine.Debugger.Break -= EngineStep; - Assert.Equal(1, countBreak); - } + Assert.Equal(1, countBreak); + } - private StepMode EngineStep(object sender, DebugInformation debugInfo) - { - Assert.NotNull(sender); - Assert.IsType(typeof(Engine), sender); - Assert.NotNull(debugInfo); + private StepMode EngineStep(object sender, DebugInformation debugInfo) + { + Assert.NotNull(sender); + Assert.IsType(typeof(Engine), sender); + Assert.NotNull(debugInfo); - countBreak++; - return stepMode; - } + countBreak++; + return stepMode; + } - [Fact] - public void ShouldShowProperDebugInformation() - { - countBreak = 0; - stepMode = StepMode.None; + [Fact] + public void ShouldShowProperDebugInformation() + { + countBreak = 0; + stepMode = StepMode.None; - var engine = new Engine(options => options.DebugMode()); - engine.Debugger.BreakPoints.Set(new BreakPoint(5, 0)); - engine.Debugger.Break += EngineStepVerifyDebugInfo; + var engine = new Engine(options => options.DebugMode()); + engine.Debugger.BreakPoints.Set(new BreakPoint(5, 0)); + engine.Debugger.Break += EngineStepVerifyDebugInfo; - engine.Evaluate(@"var global = true; + engine.Evaluate(@"var global = true; function func1() { var local = false; @@ -1570,48 +1570,48 @@ function func1() } func1();"); - engine.Debugger.Break -= EngineStepVerifyDebugInfo; + engine.Debugger.Break -= EngineStepVerifyDebugInfo; - Assert.Equal(1, countBreak); - } + Assert.Equal(1, countBreak); + } - private StepMode EngineStepVerifyDebugInfo(object sender, DebugInformation debugInfo) - { - Assert.NotNull(sender); - Assert.IsType(typeof(Engine), sender); - Assert.NotNull(debugInfo); - - Assert.NotNull(debugInfo.CallStack); - Assert.NotNull(debugInfo.CurrentNode); - Assert.NotNull(debugInfo.CurrentScopeChain); - - Assert.Equal(2, debugInfo.CallStack.Count); - Assert.Equal("func1", debugInfo.CurrentCallFrame.FunctionName); - var globalScope = debugInfo.CurrentScopeChain.Single(s => s.ScopeType == DebugScopeType.Global); - var localScope = debugInfo.CurrentScopeChain.Single(s => s.ScopeType == DebugScopeType.Local); - Assert.Contains("global", globalScope.BindingNames); - Assert.Equal(true, globalScope.GetBindingValue("global").AsBoolean()); - Assert.Contains("local", localScope.BindingNames); - Assert.Equal(false, localScope.GetBindingValue("local").AsBoolean()); - Assert.DoesNotContain("global", localScope.BindingNames); - countBreak++; - return stepMode; - } + private StepMode EngineStepVerifyDebugInfo(object sender, DebugInformation debugInfo) + { + Assert.NotNull(sender); + Assert.IsType(typeof(Engine), sender); + Assert.NotNull(debugInfo); + + Assert.NotNull(debugInfo.CallStack); + Assert.NotNull(debugInfo.CurrentNode); + Assert.NotNull(debugInfo.CurrentScopeChain); + + Assert.Equal(2, debugInfo.CallStack.Count); + Assert.Equal("func1", debugInfo.CurrentCallFrame.FunctionName); + var globalScope = debugInfo.CurrentScopeChain.Single(s => s.ScopeType == DebugScopeType.Global); + var localScope = debugInfo.CurrentScopeChain.Single(s => s.ScopeType == DebugScopeType.Local); + Assert.Contains("global", globalScope.BindingNames); + Assert.Equal(true, globalScope.GetBindingValue("global").AsBoolean()); + Assert.Contains("local", localScope.BindingNames); + Assert.Equal(false, localScope.GetBindingValue("local").AsBoolean()); + Assert.DoesNotContain("global", localScope.BindingNames); + countBreak++; + return stepMode; + } - [Fact] - public void ShouldBreakWhenConditionIsMatched() - { - countBreak = 0; - stepMode = StepMode.None; + [Fact] + public void ShouldBreakWhenConditionIsMatched() + { + countBreak = 0; + stepMode = StepMode.None; - var engine = new Engine(options => options.DebugMode()); + var engine = new Engine(options => options.DebugMode()); - engine.Debugger.Break += EngineStep; + engine.Debugger.Break += EngineStep; - engine.Debugger.BreakPoints.Set(new BreakPoint(5, 16, "condition === true")); - engine.Debugger.BreakPoints.Set(new BreakPoint(6, 16, "condition === false")); + engine.Debugger.BreakPoints.Set(new BreakPoint(5, 16, "condition === true")); + engine.Debugger.BreakPoints.Set(new BreakPoint(6, 16, "condition === false")); - engine.Evaluate(@"var local = true; + engine.Evaluate(@"var local = true; var condition = true; if (local === true) { @@ -1619,22 +1619,22 @@ public void ShouldBreakWhenConditionIsMatched() ; }"); - engine.Debugger.Break -= EngineStep; + engine.Debugger.Break -= EngineStep; - Assert.Equal(1, countBreak); - } + Assert.Equal(1, countBreak); + } - [Fact] - public void ShouldNotStepInSameLevelStatementsWhenStepOut() - { - countBreak = 0; - stepMode = StepMode.Out; + [Fact] + public void ShouldNotStepInSameLevelStatementsWhenStepOut() + { + countBreak = 0; + stepMode = StepMode.Out; - var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.Into)); + var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.Into)); - engine.Debugger.Step += EngineStep; + engine.Debugger.Step += EngineStep; - engine.Evaluate(@"function func() // first step - then stepping out + engine.Evaluate(@"function func() // first step - then stepping out { ; // shall not step ; // not even here @@ -1642,21 +1642,21 @@ public void ShouldNotStepInSameLevelStatementsWhenStepOut() func(); // shall not step ; // shall not step "); - engine.Debugger.Step -= EngineStep; + engine.Debugger.Step -= EngineStep; - Assert.Equal(1, countBreak); - } + Assert.Equal(1, countBreak); + } - [Fact] - public void ShouldNotStepInIfRequiredToStepOut() - { - countBreak = 0; + [Fact] + public void ShouldNotStepInIfRequiredToStepOut() + { + countBreak = 0; - var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.Into)); + var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.Into)); - engine.Debugger.Step += EngineStepOutWhenInsideFunction; + engine.Debugger.Step += EngineStepOutWhenInsideFunction; - engine.Evaluate(@"function func() // first step + engine.Evaluate(@"function func() // first step { ; // third step - now stepping out ; // it should not step here @@ -1664,35 +1664,35 @@ public void ShouldNotStepInIfRequiredToStepOut() func(); // second step ; // fourth step "); - engine.Debugger.Step -= EngineStepOutWhenInsideFunction; + engine.Debugger.Step -= EngineStepOutWhenInsideFunction; - Assert.Equal(4, countBreak); - } + Assert.Equal(4, countBreak); + } - private StepMode EngineStepOutWhenInsideFunction(object sender, DebugInformation debugInfo) - { - Assert.NotNull(sender); - Assert.IsType(typeof(Engine), sender); - Assert.NotNull(debugInfo); + private StepMode EngineStepOutWhenInsideFunction(object sender, DebugInformation debugInfo) + { + Assert.NotNull(sender); + Assert.IsType(typeof(Engine), sender); + Assert.NotNull(debugInfo); - countBreak++; - if (debugInfo.CallStack.Count > 1) // CallStack always has at least one element - return StepMode.Out; + countBreak++; + if (debugInfo.CallStack.Count > 1) // CallStack always has at least one element + return StepMode.Out; - return StepMode.Into; - } + return StepMode.Into; + } - [Fact] - public void ShouldBreakWhenStatementIsMultiLine() - { - countBreak = 0; - stepMode = StepMode.None; + [Fact] + public void ShouldBreakWhenStatementIsMultiLine() + { + countBreak = 0; + stepMode = StepMode.None; - var engine = new Engine(options => options.DebugMode()); - engine.Debugger.BreakPoints.Set(new BreakPoint(4, 32)); - engine.Debugger.Break += EngineStep; + var engine = new Engine(options => options.DebugMode()); + engine.Debugger.BreakPoints.Set(new BreakPoint(4, 32)); + engine.Debugger.Break += EngineStep; - engine.Evaluate(@"var global = true; + engine.Evaluate(@"var global = true; function func1() { var local = @@ -1700,22 +1700,22 @@ function func1() } func1();"); - engine.Debugger.Break -= EngineStep; + engine.Debugger.Break -= EngineStep; - Assert.Equal(1, countBreak); - } + Assert.Equal(1, countBreak); + } - [Fact] - public void ShouldNotStepInsideIfRequiredToStepOver() - { - countBreak = 0; - stepMode = StepMode.Over; + [Fact] + public void ShouldNotStepInsideIfRequiredToStepOver() + { + countBreak = 0; + stepMode = StepMode.Over; - var engine = new Engine(options => options.DebugMode().InitialStepMode(stepMode)); + var engine = new Engine(options => options.DebugMode().InitialStepMode(stepMode)); - engine.Debugger.Step += EngineStep; + engine.Debugger.Step += EngineStep; - engine.Evaluate(@"function func() // first step + engine.Evaluate(@"function func() // first step { ; // third step - it shall not step here ; // it shall not step here @@ -1723,37 +1723,37 @@ public void ShouldNotStepInsideIfRequiredToStepOver() func(); // second step ; // third step "); - engine.Debugger.Step -= EngineStep; + engine.Debugger.Step -= EngineStep; - Assert.Equal(3, countBreak); - } + Assert.Equal(3, countBreak); + } - [Fact] - public void ShouldStepAllStatementsWithoutInvocationsIfStepOver() - { - countBreak = 0; - stepMode = StepMode.Over; + [Fact] + public void ShouldStepAllStatementsWithoutInvocationsIfStepOver() + { + countBreak = 0; + stepMode = StepMode.Over; - var engine = new Engine(options => options.DebugMode().InitialStepMode(stepMode)); + var engine = new Engine(options => options.DebugMode().InitialStepMode(stepMode)); - engine.Debugger.Step += EngineStep; + engine.Debugger.Step += EngineStep; - engine.Evaluate(@"var step1 = 1; // first step + engine.Evaluate(@"var step1 = 1; // first step var step2 = 2; // second step if (step1 !== step2) // third step { ; // fourth step }"); - engine.Debugger.Step -= EngineStep; + engine.Debugger.Step -= EngineStep; - Assert.Equal(4, countBreak); - } + Assert.Equal(4, countBreak); + } - [Fact] - public void ShouldEvaluateVariableAssignmentFromLeftToRight() - { - RunTest(@" + [Fact] + public void ShouldEvaluateVariableAssignmentFromLeftToRight() + { + RunTest(@" var keys = ['a'] , source = { a: 3} , target = {} @@ -1764,12 +1764,12 @@ public void ShouldEvaluateVariableAssignmentFromLeftToRight() equal('a', key); equal(3, target[key]); "); - } + } - [Fact] - public void ObjectShouldBeExtensible() - { - RunTest(@" + [Fact] + public void ObjectShouldBeExtensible() + { + RunTest(@" try { Object.defineProperty(Object.defineProperty, 'foo', { value: 1 }); } @@ -1777,36 +1777,36 @@ public void ObjectShouldBeExtensible() assert(false); } "); - } + } - [Fact] - public void ArrayIndexShouldBeConvertedToUint32() - { - // This is missing from ECMA tests suite - // http://www.ecma-international.org/ecma-262/5.1/#sec-15.4 + [Fact] + public void ArrayIndexShouldBeConvertedToUint32() + { + // This is missing from ECMA tests suite + // http://www.ecma-international.org/ecma-262/5.1/#sec-15.4 - RunTest(@" + RunTest(@" var a = [ 'foo' ]; assert(a[0] === 'foo'); assert(a['0'] === 'foo'); assert(a['00'] === undefined); "); - } + } - [Fact] - public void HexZeroAsArrayIndexShouldWork() - { - var engine = new Engine(); - engine.Evaluate("var t = '1234'; var value = null;"); - Assert.Equal("1", engine.Execute("value = t[0x0];").GetValue("value").AsString()); - Assert.Equal("1", engine.Execute("value = t[0];").GetValue("value").AsString()); - Assert.Equal("1", engine.Execute("value = t['0'];").GetValue("value").AsString()); - } + [Fact] + public void HexZeroAsArrayIndexShouldWork() + { + var engine = new Engine(); + engine.Evaluate("var t = '1234'; var value = null;"); + Assert.Equal("1", engine.Execute("value = t[0x0];").GetValue("value").AsString()); + Assert.Equal("1", engine.Execute("value = t[0];").GetValue("value").AsString()); + Assert.Equal("1", engine.Execute("value = t['0'];").GetValue("value").AsString()); + } - [Fact] - public void DatePrototypeFunctionWorkOnDateOnly() - { - RunTest(@" + [Fact] + public void DatePrototypeFunctionWorkOnDateOnly() + { + RunTest(@" try { var myObj = Object.create(Date.prototype); myObj.toDateString(); @@ -1814,23 +1814,23 @@ public void DatePrototypeFunctionWorkOnDateOnly() assert(e instanceof TypeError); } "); - } + } - [Fact] - public void DateToStringMethodsShouldUseCurrentTimeZoneAndCulture() - { - // Forcing to PDT and FR for tests - // var PDT = TimeZoneInfo.CreateCustomTimeZone("Pacific Daylight Time", new TimeSpan(-7, 0, 0), "Pacific Daylight Time", "Pacific Daylight Time"); - var PDT = _pacificTimeZone; - var FR = new CultureInfo("fr-FR"); + [Fact] + public void DateToStringMethodsShouldUseCurrentTimeZoneAndCulture() + { + // Forcing to PDT and FR for tests + // var PDT = TimeZoneInfo.CreateCustomTimeZone("Pacific Daylight Time", new TimeSpan(-7, 0, 0), "Pacific Daylight Time", "Pacific Daylight Time"); + var PDT = _pacificTimeZone; + var FR = new CultureInfo("fr-FR"); - var engine = new Engine(options => options.LocalTimeZone(PDT).Culture(FR)) + var engine = new Engine(options => options.LocalTimeZone(PDT).Culture(FR)) .SetValue("log", new Action(Console.WriteLine)) .SetValue("assert", new Action(Assert.True)) .SetValue("equal", new Action(Assert.Equal)) - ; + ; - engine.Evaluate(@" + engine.Evaluate(@" var d = new Date(1433160000000); equal('Mon Jun 01 2015 05:00:00 GMT-0700 (Pacific Standard Time)', d.toString()); @@ -1840,51 +1840,51 @@ public void DateToStringMethodsShouldUseCurrentTimeZoneAndCulture() equal('lundi 1 juin 2015', d.toLocaleDateString()); equal('05:00:00', d.toLocaleTimeString()); "); - } + } - [Fact] - public void DateShouldHonorTimezoneDaylightSavingRules() - { - var EST = _easternTimeZone; - var engine = new Engine(options => options.LocalTimeZone(EST)) - .SetValue("log", new Action(Console.WriteLine)) - .SetValue("assert", new Action(Assert.True)) - .SetValue("equal", new Action(Assert.Equal)); + [Fact] + public void DateShouldHonorTimezoneDaylightSavingRules() + { + var EST = _easternTimeZone; + var engine = new Engine(options => options.LocalTimeZone(EST)) + .SetValue("log", new Action(Console.WriteLine)) + .SetValue("assert", new Action(Assert.True)) + .SetValue("equal", new Action(Assert.Equal)); - engine.Evaluate(@" + engine.Evaluate(@" var d = new Date(2016, 8, 1); // there's a Linux difference, so do a replace equal('Thu Sep 01 2016 00:00:00 GMT-0400 (US Eastern Standard Time)', d.toString().replace('(Eastern Standard Time)', '(US Eastern Standard Time)')); equal('Thu Sep 01 2016', d.toDateString()); "); - } - - [Fact] - public void DateShouldParseToString() - { - // Forcing to PDT and FR for tests - // var PDT = TimeZoneInfo.CreateCustomTimeZone("Pacific Daylight Time", new TimeSpan(-7, 0, 0), "Pacific Daylight Time", "Pacific Daylight Time"); - var PDT = _pacificTimeZone; - var FR = new CultureInfo("fr-FR"); + } - new Engine(options => options.LocalTimeZone(PDT).Culture(FR)) - .SetValue("log", new Action(Console.WriteLine)) - .SetValue("assert", new Action(Assert.True)) - .SetValue("equal", new Action(Assert.Equal)) - .Evaluate(@" + [Fact] + public void DateShouldParseToString() + { + // Forcing to PDT and FR for tests + // var PDT = TimeZoneInfo.CreateCustomTimeZone("Pacific Daylight Time", new TimeSpan(-7, 0, 0), "Pacific Daylight Time", "Pacific Daylight Time"); + var PDT = _pacificTimeZone; + var FR = new CultureInfo("fr-FR"); + + new Engine(options => options.LocalTimeZone(PDT).Culture(FR)) + .SetValue("log", new Action(Console.WriteLine)) + .SetValue("assert", new Action(Assert.True)) + .SetValue("equal", new Action(Assert.Equal)) + .Evaluate(@" var d = new Date(1433160000000); equal(Date.parse(d.toString()), d.valueOf()); equal(Date.parse(d.toLocaleString()), d.valueOf()); "); - } + } - [Fact] - public void ShouldThrowErrorWhenMaxExecutionStackCountLimitExceeded() - { - new Engine(options => options.Constraints.MaxExecutionStackCount = 1000) - .SetValue("assert", new Action(Assert.True)) - .Evaluate(@" + [Fact] + public void ShouldThrowErrorWhenMaxExecutionStackCountLimitExceeded() + { + new Engine(options => options.Constraints.MaxExecutionStackCount = 1000) + .SetValue("assert", new Action(Assert.True)) + .Evaluate(@" var count = 0; function recurse() { count++; @@ -1900,54 +1900,54 @@ function recurse() { } "); - } - - - [Fact] - public void LocaleNumberShouldUseLocalCulture() - { - // Forcing to PDT and FR for tests - // var PDT = TimeZoneInfo.CreateCustomTimeZone("Pacific Daylight Time", new TimeSpan(-7, 0, 0), "Pacific Daylight Time", "Pacific Daylight Time"); - var PDT = _pacificTimeZone; - var FR = new CultureInfo("fr-FR"); - - var engine = new Engine(options => options.LocalTimeZone(PDT).Culture(FR)) - .SetValue("log", new Action(Console.WriteLine)) - .SetValue("assert", new Action(Assert.True)) - .SetValue("equal", new Action(Assert.Equal)); + } - engine.Evaluate("var d = new Number(-1.23);"); - engine.Evaluate("equal('-1.23', d.toString());"); - // NET 5 globalization APIs use ICU libraries on newer Windows 10 giving different result - // build server is older Windows... - engine.Evaluate("assert('-1,230' === d.toLocaleString() || '-1,23' === d.toLocaleString());"); - } + [Fact] + public void LocaleNumberShouldUseLocalCulture() + { + // Forcing to PDT and FR for tests + // var PDT = TimeZoneInfo.CreateCustomTimeZone("Pacific Daylight Time", new TimeSpan(-7, 0, 0), "Pacific Daylight Time", "Pacific Daylight Time"); + var PDT = _pacificTimeZone; + var FR = new CultureInfo("fr-FR"); + + var engine = new Engine(options => options.LocalTimeZone(PDT).Culture(FR)) + .SetValue("log", new Action(Console.WriteLine)) + .SetValue("assert", new Action(Assert.True)) + .SetValue("equal", new Action(Assert.Equal)); + + engine.Evaluate("var d = new Number(-1.23);"); + engine.Evaluate("equal('-1.23', d.toString());"); + + // NET 5 globalization APIs use ICU libraries on newer Windows 10 giving different result + // build server is older Windows... + engine.Evaluate("assert('-1,230' === d.toLocaleString() || '-1,23' === d.toLocaleString());"); + } - [Fact] - public void DateCtorShouldAcceptDate() - { - RunTest(@" + [Fact] + public void DateCtorShouldAcceptDate() + { + RunTest(@" var a = new Date(); var b = new Date(a); assert(String(a) === String(b)); "); - } + } - [Fact] - public void RegExpResultIsMutable() - { - RunTest(@" + [Fact] + public void RegExpResultIsMutable() + { + RunTest(@" var match = /quick\s(brown).+?(jumps)/ig.exec('The Quick Brown Fox Jumps Over The Lazy Dog'); var result = match.shift(); assert(result === 'Quick Brown Fox Jumps'); "); - } + } - [Fact] - public void RegExpSupportsMultiline() - { - RunTest(@" + [Fact] + public void RegExpSupportsMultiline() + { + RunTest(@" var rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg; var headersString = 'X-AspNetMvc-Version: 4.0\r\nX-Powered-By: ASP.NET\r\n\r\n'; match = rheaders.exec(headersString); @@ -1955,7 +1955,7 @@ public void RegExpSupportsMultiline() assert('4.0' === match[2]); "); - RunTest(@" + RunTest(@" var rheaders = /^(.*?):[ \t]*(.*?)$/mg; var headersString = 'X-AspNetMvc-Version: 4.0\r\nX-Powered-By: ASP.NET\r\n\r\n'; match = rheaders.exec(headersString); @@ -1963,62 +1963,62 @@ public void RegExpSupportsMultiline() assert('4.0' === match[2]); "); - RunTest(@" + RunTest(@" var rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg; var headersString = 'X-AspNetMvc-Version: 4.0\nX-Powered-By: ASP.NET\n\n'; match = rheaders.exec(headersString); assert('X-AspNetMvc-Version' === match[1]); assert('4.0' === match[2]); "); - } + } - [Fact] - public void RegExpPrototypeToString() - { - RunTest("assert(RegExp.prototype.toString() === '/(?:)/');"); - } + [Fact] + public void RegExpPrototypeToString() + { + RunTest("assert(RegExp.prototype.toString() === '/(?:)/');"); + } - [Fact] - public void ShouldSetYearBefore1970() - { + [Fact] + public void ShouldSetYearBefore1970() + { - RunTest(@" + RunTest(@" var d = new Date('1969-01-01T08:17:00Z'); d.setYear(2015); equal('2015-01-01T08:17:00.000Z', d.toISOString()); "); - } + } - [Fact] - public void ShouldUseReplaceMarkers() - { - RunTest(@" + [Fact] + public void ShouldUseReplaceMarkers() + { + RunTest(@" var re = /a/g; var str = 'abab'; var newstr = str.replace(re, '$\'x'); equal('babxbbxb', newstr); "); - } + } - [Fact] - public void ExceptionShouldHaveLocationOfInnerFunction() - { - var engine = new Engine(); - const string source = @" + [Fact] + public void ExceptionShouldHaveLocationOfInnerFunction() + { + var engine = new Engine(); + const string source = @" function test(s) { o.boom(); } test('arg'); "; - var ex = Assert.Throws(() => engine.Evaluate(source)); - Assert.Equal(3, ex.Location.Start.Line); - } + var ex = Assert.Throws(() => engine.Evaluate(source)); + Assert.Equal(3, ex.Location.Start.Line); + } - [Fact] - public void GlobalRegexLiteralShouldNotKeepState() - { - RunTest(@" + [Fact] + public void GlobalRegexLiteralShouldNotKeepState() + { + RunTest(@" var url = 'https://www.example.com'; assert(isAbsolutePath(url)); @@ -2029,61 +2029,61 @@ function isAbsolutePath(path) { return /\.+/g.test(path); } "); - } + } - [Fact] - public void ShouldCompareInnerValueOfClrInstances() - { - var engine = new Engine(); + [Fact] + public void ShouldCompareInnerValueOfClrInstances() + { + var engine = new Engine(); - // Create two separate Guid with identical inner values. - var guid1 = Guid.NewGuid(); - var guid2 = new Guid(guid1.ToString()); + // Create two separate Guid with identical inner values. + var guid1 = Guid.NewGuid(); + var guid2 = new Guid(guid1.ToString()); - engine.SetValue("guid1", guid1); - engine.SetValue("guid2", guid2); + engine.SetValue("guid1", guid1); + engine.SetValue("guid2", guid2); - var result = engine.Evaluate("guid1 == guid2").AsBoolean(); + var result = engine.Evaluate("guid1 == guid2").AsBoolean(); - Assert.True(result); - } + Assert.True(result); + } - [Fact] - public void CanStringifyToConsole() - { - var engine = new Engine(options => options.AllowClr(typeof(Console).Assembly)); - engine.Evaluate("System.Console.WriteLine(JSON.stringify({x:12, y:14}));"); - } + [Fact] + public void CanStringifyToConsole() + { + var engine = new Engine(options => options.AllowClr(typeof(Console).Assembly)); + engine.Evaluate("System.Console.WriteLine(JSON.stringify({x:12, y:14}));"); + } - [Fact] - public void ShouldNotCompareClrInstancesWithObjects() - { - var engine = new Engine(); + [Fact] + public void ShouldNotCompareClrInstancesWithObjects() + { + var engine = new Engine(); - var guid1 = Guid.NewGuid(); + var guid1 = Guid.NewGuid(); - engine.SetValue("guid1", guid1); + engine.SetValue("guid1", guid1); - var result = engine.Evaluate("guid1 == {}").AsBoolean(); + var result = engine.Evaluate("guid1 == {}").AsBoolean(); - Assert.False(result); - } + Assert.False(result); + } - [Fact] - public void ShouldStringifyNumWithoutV8DToA() - { - // 53.6841659 cannot be converted by V8's DToA => "old" DToA code will be used. - var engine = new Engine(); - var val = engine.Evaluate("JSON.stringify(53.6841659)"); + [Fact] + public void ShouldStringifyNumWithoutV8DToA() + { + // 53.6841659 cannot be converted by V8's DToA => "old" DToA code will be used. + var engine = new Engine(); + var val = engine.Evaluate("JSON.stringify(53.6841659)"); - Assert.Equal("53.6841659", val.AsString()); - } + Assert.Equal("53.6841659", val.AsString()); + } - [Fact] - public void ShouldStringifyObjectWithPropertiesToSameRef() - { - var engine = new Engine(); - var res = engine.Evaluate(@" + [Fact] + public void ShouldStringifyObjectWithPropertiesToSameRef() + { + var engine = new Engine(); + var res = engine.Evaluate(@" var obj = { a : [], a1 : ['str'], @@ -2095,14 +2095,14 @@ public void ShouldStringifyObjectWithPropertiesToSameRef() JSON.stringify(obj); "); - Assert.True(res == "{\"a\":[],\"a1\":[\"str\"],\"a2\":{},\"a3\":{\"prop\":\"val\"},\"b\":[],\"b1\":[\"str\"]}"); - } + Assert.True(res == "{\"a\":[],\"a1\":[\"str\"],\"a2\":{},\"a3\":{\"prop\":\"val\"},\"b\":[],\"b1\":[\"str\"]}"); + } - [Fact] - public void ShouldThrowOnSerializingCyclicRefObject() - { - var engine = new Engine(); - var res = engine.Evaluate(@" + [Fact] + public void ShouldThrowOnSerializingCyclicRefObject() + { + var engine = new Engine(); + var res = engine.Evaluate(@" (function(){ try{ a = []; @@ -2115,473 +2115,473 @@ public void ShouldThrowOnSerializingCyclicRefObject() })(); "); - Assert.True(res == "Cyclic reference detected."); - } + Assert.True(res == "Cyclic reference detected."); + } - [Fact] - public void ShouldNotStringifyFunctionValuedProperties() - { - var engine = new Engine(); - var res = engine.Evaluate(@" + [Fact] + public void ShouldNotStringifyFunctionValuedProperties() + { + var engine = new Engine(); + var res = engine.Evaluate(@" var obj = { f: function() { } }; return JSON.stringify(obj); "); - Assert.Equal("{}", res.AsString()); - } + Assert.Equal("{}", res.AsString()); + } - [Theory] - [InlineData("", "escape('')")] - [InlineData("%u0100%u0101%u0102", "escape('\u0100\u0101\u0102')")] - [InlineData("%uFFFD%uFFFE%uFFFF", "escape('\ufffd\ufffe\uffff')")] - [InlineData("%uD834%uDF06", "escape('\ud834\udf06')")] - [InlineData("%00%01%02%03", "escape('\x00\x01\x02\x03')")] - [InlineData("%2C", "escape(',')")] - [InlineData("%3A%3B%3C%3D%3E%3F", "escape(':;<=>?')")] - [InlineData("%60", "escape('`')")] - [InlineData("%7B%7C%7D%7E%7F%80", "escape('{|}~\x7f\x80')")] - [InlineData("%FD%FE%FF", "escape('\xfd\xfe\xff')")] - [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./", "escape('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./')")] - public void ShouldEvaluateEscape(object expected, string source) - { - var engine = new Engine(); - var result = engine.Evaluate(source).ToObject(); + [Theory] + [InlineData("", "escape('')")] + [InlineData("%u0100%u0101%u0102", "escape('\u0100\u0101\u0102')")] + [InlineData("%uFFFD%uFFFE%uFFFF", "escape('\ufffd\ufffe\uffff')")] + [InlineData("%uD834%uDF06", "escape('\ud834\udf06')")] + [InlineData("%00%01%02%03", "escape('\x00\x01\x02\x03')")] + [InlineData("%2C", "escape(',')")] + [InlineData("%3A%3B%3C%3D%3E%3F", "escape(':;<=>?')")] + [InlineData("%60", "escape('`')")] + [InlineData("%7B%7C%7D%7E%7F%80", "escape('{|}~\x7f\x80')")] + [InlineData("%FD%FE%FF", "escape('\xfd\xfe\xff')")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./", "escape('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./')")] + public void ShouldEvaluateEscape(object expected, string source) + { + var engine = new Engine(); + var result = engine.Evaluate(source).ToObject(); - Assert.Equal(expected, result); - } + Assert.Equal(expected, result); + } - [Theory] - //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/empty-string.js - [InlineData("", "unescape('')")] - //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four-ignore-bad-u.js - [InlineData("%U0000", "unescape('%U0000')")] - [InlineData("%t0000", "unescape('%t0000')")] - [InlineData("%v0000", "unescape('%v0000')")] - [InlineData("%" + "\x00" + "00", "unescape('%%0000')")] - //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four-ignore-end-str.js - [InlineData("%u", "unescape('%u')")] - [InlineData("%u0", "unescape('%u0')")] - [InlineData("%u1", "unescape('%u1')")] - [InlineData("%u2", "unescape('%u2')")] - [InlineData("%u3", "unescape('%u3')")] - [InlineData("%u4", "unescape('%u4')")] - [InlineData("%u5", "unescape('%u5')")] - [InlineData("%u6", "unescape('%u6')")] - [InlineData("%u7", "unescape('%u7')")] - [InlineData("%u8", "unescape('%u8')")] - [InlineData("%u9", "unescape('%u9')")] - [InlineData("%ua", "unescape('%ua')")] - [InlineData("%uA", "unescape('%uA')")] - [InlineData("%ub", "unescape('%ub')")] - [InlineData("%uB", "unescape('%uB')")] - [InlineData("%uc", "unescape('%uc')")] - [InlineData("%uC", "unescape('%uC')")] - [InlineData("%ud", "unescape('%ud')")] - [InlineData("%uD", "unescape('%uD')")] - [InlineData("%ue", "unescape('%ue')")] - [InlineData("%uE", "unescape('%uE')")] - [InlineData("%uf", "unescape('%uf')")] - [InlineData("%uF", "unescape('%uF')")] - [InlineData("%u01", "unescape('%u01')")] - [InlineData("%u02", "unescape('%u02')")] - [InlineData("%u03", "unescape('%u03')")] - [InlineData("%u04", "unescape('%u04')")] - [InlineData("%u05", "unescape('%u05')")] - [InlineData("%u06", "unescape('%u06')")] - [InlineData("%u07", "unescape('%u07')")] - [InlineData("%u08", "unescape('%u08')")] - [InlineData("%u09", "unescape('%u09')")] - [InlineData("%u0a", "unescape('%u0a')")] - [InlineData("%u0A", "unescape('%u0A')")] - [InlineData("%u0b", "unescape('%u0b')")] - [InlineData("%u0B", "unescape('%u0B')")] - [InlineData("%u0c", "unescape('%u0c')")] - [InlineData("%u0C", "unescape('%u0C')")] - [InlineData("%u0d", "unescape('%u0d')")] - [InlineData("%u0D", "unescape('%u0D')")] - [InlineData("%u0e", "unescape('%u0e')")] - [InlineData("%u0E", "unescape('%u0E')")] - [InlineData("%u0f", "unescape('%u0f')")] - [InlineData("%u0F", "unescape('%u0F')")] - [InlineData("%u000", "unescape('%u000')")] - [InlineData("%u001", "unescape('%u001')")] - [InlineData("%u002", "unescape('%u002')")] - [InlineData("%u003", "unescape('%u003')")] - [InlineData("%u004", "unescape('%u004')")] - [InlineData("%u005", "unescape('%u005')")] - [InlineData("%u006", "unescape('%u006')")] - [InlineData("%u007", "unescape('%u007')")] - [InlineData("%u008", "unescape('%u008')")] - [InlineData("%u009", "unescape('%u009')")] - [InlineData("%u00a", "unescape('%u00a')")] - [InlineData("%u00A", "unescape('%u00A')")] - [InlineData("%u00b", "unescape('%u00b')")] - [InlineData("%u00B", "unescape('%u00B')")] - [InlineData("%u00c", "unescape('%u00c')")] - [InlineData("%u00C", "unescape('%u00C')")] - [InlineData("%u00d", "unescape('%u00d')")] - [InlineData("%u00D", "unescape('%u00D')")] - [InlineData("%u00e", "unescape('%u00e')")] - [InlineData("%u00E", "unescape('%u00E')")] - [InlineData("%u00f", "unescape('%u00f')")] - [InlineData("%u00F", "unescape('%u00F')")] - //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four-ignore-non-hex.js - [InlineData("%u000%0", "unescape('%u000%0')")] - [InlineData("%u000g0", "unescape('%u000g0')")] - [InlineData("%u000G0", "unescape('%u000G0')")] - [InlineData("%u00g00", "unescape('%u00g00')")] - [InlineData("%u00G00", "unescape('%u00G00')")] - [InlineData("%u0g000", "unescape('%u0g000')")] - [InlineData("%u0G000", "unescape('%u0G000')")] - [InlineData("%ug0000", "unescape('%ug0000')")] - [InlineData("%uG0000", "unescape('%uG0000')")] - [InlineData("%u000u0", "unescape('%u000u0')")] - [InlineData("%u000U0", "unescape('%u000U0')")] - [InlineData("%u00u00", "unescape('%u00u00')")] - [InlineData("%u00U00", "unescape('%u00U00')")] - [InlineData("%u0u000", "unescape('%u0u000')")] - [InlineData("%u0U000", "unescape('%u0U000')")] - [InlineData("%uu0000", "unescape('%uu0000')")] - [InlineData("%uU0000", "unescape('%uU0000')")] - //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four.js - [InlineData("%0" + "\x00" + "0", "unescape('%0%u00000')")] - [InlineData("%0" + "\x01" + "0", "unescape('%0%u00010')")] - [InlineData("%0)0", "unescape('%0%u00290')")] - [InlineData("%0*0", "unescape('%0%u002a0')")] - [InlineData("%0*0", "unescape('%0%u002A0')")] - [InlineData("%0+0", "unescape('%0%u002b0')")] - [InlineData("%0+0", "unescape('%0%u002B0')")] - [InlineData("%0,0", "unescape('%0%u002c0')")] - [InlineData("%0,0", "unescape('%0%u002C0')")] - [InlineData("%0-0", "unescape('%0%u002d0')")] - [InlineData("%0-0", "unescape('%0%u002D0')")] - [InlineData("%090", "unescape('%0%u00390')")] - [InlineData("%0:0", "unescape('%0%u003a0')")] - [InlineData("%0:0", "unescape('%0%u003A0')")] - [InlineData("%0?0", "unescape('%0%u003f0')")] - [InlineData("%0?0", "unescape('%0%u003F0')")] - [InlineData("%0@0", "unescape('%0%u00400')")] - [InlineData("%0Z0", "unescape('%0%u005a0')")] - [InlineData("%0Z0", "unescape('%0%u005A0')")] - [InlineData("%0[0", "unescape('%0%u005b0')")] - [InlineData("%0[0", "unescape('%0%u005B0')")] - [InlineData("%0^0", "unescape('%0%u005e0')")] - [InlineData("%0^0", "unescape('%0%u005E0')")] - [InlineData("%0_0", "unescape('%0%u005f0')")] - [InlineData("%0_0", "unescape('%0%u005F0')")] - [InlineData("%0`0", "unescape('%0%u00600')")] - [InlineData("%0a0", "unescape('%0%u00610')")] - [InlineData("%0z0", "unescape('%0%u007a0')")] - [InlineData("%0z0", "unescape('%0%u007A0')")] - [InlineData("%0{0", "unescape('%0%u007b0')")] - [InlineData("%0{0", "unescape('%0%u007B0')")] - [InlineData("%0" + "\ufffe" + "0", "unescape('%0%ufffe0')")] - [InlineData("%0" + "\ufffe" + "0", "unescape('%0%uFffe0')")] - [InlineData("%0" + "\ufffe" + "0", "unescape('%0%ufFfe0')")] - [InlineData("%0" + "\ufffe" + "0", "unescape('%0%uffFe0')")] - [InlineData("%0" + "\ufffe" + "0", "unescape('%0%ufffE0')")] - [InlineData("%0" + "\ufffe" + "0", "unescape('%0%uFFFE0')")] - [InlineData("%0" + "\uffff" + "0", "unescape('%0%uffff0')")] - [InlineData("%0" + "\uffff" + "0", "unescape('%0%uFfff0')")] - [InlineData("%0" + "\uffff" + "0", "unescape('%0%ufFff0')")] - [InlineData("%0" + "\uffff" + "0", "unescape('%0%uffFf0')")] - [InlineData("%0" + "\uffff" + "0", "unescape('%0%ufffF0')")] - [InlineData("%0" + "\uffff" + "0", "unescape('%0%uFFFF0')")] - //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/two-ignore-end-str.js - [InlineData("%", "unescape('%')")] - [InlineData("%0", "unescape('%0')")] - [InlineData("%1", "unescape('%1')")] - [InlineData("%2", "unescape('%2')")] - [InlineData("%3", "unescape('%3')")] - [InlineData("%4", "unescape('%4')")] - [InlineData("%5", "unescape('%5')")] - [InlineData("%6", "unescape('%6')")] - [InlineData("%7", "unescape('%7')")] - [InlineData("%8", "unescape('%8')")] - [InlineData("%9", "unescape('%9')")] - [InlineData("%a", "unescape('%a')")] - [InlineData("%A", "unescape('%A')")] - [InlineData("%b", "unescape('%b')")] - [InlineData("%B", "unescape('%B')")] - [InlineData("%c", "unescape('%c')")] - [InlineData("%C", "unescape('%C')")] - [InlineData("%d", "unescape('%d')")] - [InlineData("%D", "unescape('%D')")] - [InlineData("%e", "unescape('%e')")] - [InlineData("%E", "unescape('%E')")] - [InlineData("%f", "unescape('%f')")] - [InlineData("%F", "unescape('%F')")] - //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/two-ignore-non-hex.js - [InlineData("%0%0", "unescape('%0%0')")] - [InlineData("%0g0", "unescape('%0g0')")] - [InlineData("%0G0", "unescape('%0G0')")] - [InlineData("%g00", "unescape('%g00')")] - [InlineData("%G00", "unescape('%G00')")] - [InlineData("%0u0", "unescape('%0u0')")] - [InlineData("%0U0", "unescape('%0U0')")] - [InlineData("%u00", "unescape('%u00')")] - [InlineData("%U00", "unescape('%U00')")] - //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/two.js - [InlineData("%0" + "\x00" + "00", "unescape('%0%0000')")] - [InlineData("%0" + "\x01" + "00", "unescape('%0%0100')")] - [InlineData("%0)00", "unescape('%0%2900')")] - [InlineData("%0*00", "unescape('%0%2a00')")] - [InlineData("%0*00", "unescape('%0%2A00')")] - [InlineData("%0+00", "unescape('%0%2b00')")] - [InlineData("%0+00", "unescape('%0%2B00')")] - [InlineData("%0,00", "unescape('%0%2c00')")] - [InlineData("%0,00", "unescape('%0%2C00')")] - [InlineData("%0-00", "unescape('%0%2d00')")] - [InlineData("%0-00", "unescape('%0%2D00')")] - [InlineData("%0900", "unescape('%0%3900')")] - [InlineData("%0:00", "unescape('%0%3a00')")] - [InlineData("%0:00", "unescape('%0%3A00')")] - [InlineData("%0?00", "unescape('%0%3f00')")] - [InlineData("%0?00", "unescape('%0%3F00')")] - [InlineData("%0@00", "unescape('%0%4000')")] - [InlineData("%0Z00", "unescape('%0%5a00')")] - [InlineData("%0Z00", "unescape('%0%5A00')")] - [InlineData("%0[00", "unescape('%0%5b00')")] - [InlineData("%0[00", "unescape('%0%5B00')")] - [InlineData("%0^00", "unescape('%0%5e00')")] - [InlineData("%0^00", "unescape('%0%5E00')")] - [InlineData("%0_00", "unescape('%0%5f00')")] - [InlineData("%0_00", "unescape('%0%5F00')")] - [InlineData("%0`00", "unescape('%0%6000')")] - [InlineData("%0a00", "unescape('%0%6100')")] - [InlineData("%0z00", "unescape('%0%7a00')")] - [InlineData("%0z00", "unescape('%0%7A00')")] - [InlineData("%0{00", "unescape('%0%7b00')")] - [InlineData("%0{00", "unescape('%0%7B00')")] - public void ShouldEvaluateUnescape(object expected, string source) - { - var engine = new Engine(); - var result = engine.Evaluate(source).ToObject(); + [Theory] + //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/empty-string.js + [InlineData("", "unescape('')")] + //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four-ignore-bad-u.js + [InlineData("%U0000", "unescape('%U0000')")] + [InlineData("%t0000", "unescape('%t0000')")] + [InlineData("%v0000", "unescape('%v0000')")] + [InlineData("%" + "\x00" + "00", "unescape('%%0000')")] + //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four-ignore-end-str.js + [InlineData("%u", "unescape('%u')")] + [InlineData("%u0", "unescape('%u0')")] + [InlineData("%u1", "unescape('%u1')")] + [InlineData("%u2", "unescape('%u2')")] + [InlineData("%u3", "unescape('%u3')")] + [InlineData("%u4", "unescape('%u4')")] + [InlineData("%u5", "unescape('%u5')")] + [InlineData("%u6", "unescape('%u6')")] + [InlineData("%u7", "unescape('%u7')")] + [InlineData("%u8", "unescape('%u8')")] + [InlineData("%u9", "unescape('%u9')")] + [InlineData("%ua", "unescape('%ua')")] + [InlineData("%uA", "unescape('%uA')")] + [InlineData("%ub", "unescape('%ub')")] + [InlineData("%uB", "unescape('%uB')")] + [InlineData("%uc", "unescape('%uc')")] + [InlineData("%uC", "unescape('%uC')")] + [InlineData("%ud", "unescape('%ud')")] + [InlineData("%uD", "unescape('%uD')")] + [InlineData("%ue", "unescape('%ue')")] + [InlineData("%uE", "unescape('%uE')")] + [InlineData("%uf", "unescape('%uf')")] + [InlineData("%uF", "unescape('%uF')")] + [InlineData("%u01", "unescape('%u01')")] + [InlineData("%u02", "unescape('%u02')")] + [InlineData("%u03", "unescape('%u03')")] + [InlineData("%u04", "unescape('%u04')")] + [InlineData("%u05", "unescape('%u05')")] + [InlineData("%u06", "unescape('%u06')")] + [InlineData("%u07", "unescape('%u07')")] + [InlineData("%u08", "unescape('%u08')")] + [InlineData("%u09", "unescape('%u09')")] + [InlineData("%u0a", "unescape('%u0a')")] + [InlineData("%u0A", "unescape('%u0A')")] + [InlineData("%u0b", "unescape('%u0b')")] + [InlineData("%u0B", "unescape('%u0B')")] + [InlineData("%u0c", "unescape('%u0c')")] + [InlineData("%u0C", "unescape('%u0C')")] + [InlineData("%u0d", "unescape('%u0d')")] + [InlineData("%u0D", "unescape('%u0D')")] + [InlineData("%u0e", "unescape('%u0e')")] + [InlineData("%u0E", "unescape('%u0E')")] + [InlineData("%u0f", "unescape('%u0f')")] + [InlineData("%u0F", "unescape('%u0F')")] + [InlineData("%u000", "unescape('%u000')")] + [InlineData("%u001", "unescape('%u001')")] + [InlineData("%u002", "unescape('%u002')")] + [InlineData("%u003", "unescape('%u003')")] + [InlineData("%u004", "unescape('%u004')")] + [InlineData("%u005", "unescape('%u005')")] + [InlineData("%u006", "unescape('%u006')")] + [InlineData("%u007", "unescape('%u007')")] + [InlineData("%u008", "unescape('%u008')")] + [InlineData("%u009", "unescape('%u009')")] + [InlineData("%u00a", "unescape('%u00a')")] + [InlineData("%u00A", "unescape('%u00A')")] + [InlineData("%u00b", "unescape('%u00b')")] + [InlineData("%u00B", "unescape('%u00B')")] + [InlineData("%u00c", "unescape('%u00c')")] + [InlineData("%u00C", "unescape('%u00C')")] + [InlineData("%u00d", "unescape('%u00d')")] + [InlineData("%u00D", "unescape('%u00D')")] + [InlineData("%u00e", "unescape('%u00e')")] + [InlineData("%u00E", "unescape('%u00E')")] + [InlineData("%u00f", "unescape('%u00f')")] + [InlineData("%u00F", "unescape('%u00F')")] + //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four-ignore-non-hex.js + [InlineData("%u000%0", "unescape('%u000%0')")] + [InlineData("%u000g0", "unescape('%u000g0')")] + [InlineData("%u000G0", "unescape('%u000G0')")] + [InlineData("%u00g00", "unescape('%u00g00')")] + [InlineData("%u00G00", "unescape('%u00G00')")] + [InlineData("%u0g000", "unescape('%u0g000')")] + [InlineData("%u0G000", "unescape('%u0G000')")] + [InlineData("%ug0000", "unescape('%ug0000')")] + [InlineData("%uG0000", "unescape('%uG0000')")] + [InlineData("%u000u0", "unescape('%u000u0')")] + [InlineData("%u000U0", "unescape('%u000U0')")] + [InlineData("%u00u00", "unescape('%u00u00')")] + [InlineData("%u00U00", "unescape('%u00U00')")] + [InlineData("%u0u000", "unescape('%u0u000')")] + [InlineData("%u0U000", "unescape('%u0U000')")] + [InlineData("%uu0000", "unescape('%uu0000')")] + [InlineData("%uU0000", "unescape('%uU0000')")] + //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four.js + [InlineData("%0" + "\x00" + "0", "unescape('%0%u00000')")] + [InlineData("%0" + "\x01" + "0", "unescape('%0%u00010')")] + [InlineData("%0)0", "unescape('%0%u00290')")] + [InlineData("%0*0", "unescape('%0%u002a0')")] + [InlineData("%0*0", "unescape('%0%u002A0')")] + [InlineData("%0+0", "unescape('%0%u002b0')")] + [InlineData("%0+0", "unescape('%0%u002B0')")] + [InlineData("%0,0", "unescape('%0%u002c0')")] + [InlineData("%0,0", "unescape('%0%u002C0')")] + [InlineData("%0-0", "unescape('%0%u002d0')")] + [InlineData("%0-0", "unescape('%0%u002D0')")] + [InlineData("%090", "unescape('%0%u00390')")] + [InlineData("%0:0", "unescape('%0%u003a0')")] + [InlineData("%0:0", "unescape('%0%u003A0')")] + [InlineData("%0?0", "unescape('%0%u003f0')")] + [InlineData("%0?0", "unescape('%0%u003F0')")] + [InlineData("%0@0", "unescape('%0%u00400')")] + [InlineData("%0Z0", "unescape('%0%u005a0')")] + [InlineData("%0Z0", "unescape('%0%u005A0')")] + [InlineData("%0[0", "unescape('%0%u005b0')")] + [InlineData("%0[0", "unescape('%0%u005B0')")] + [InlineData("%0^0", "unescape('%0%u005e0')")] + [InlineData("%0^0", "unescape('%0%u005E0')")] + [InlineData("%0_0", "unescape('%0%u005f0')")] + [InlineData("%0_0", "unescape('%0%u005F0')")] + [InlineData("%0`0", "unescape('%0%u00600')")] + [InlineData("%0a0", "unescape('%0%u00610')")] + [InlineData("%0z0", "unescape('%0%u007a0')")] + [InlineData("%0z0", "unescape('%0%u007A0')")] + [InlineData("%0{0", "unescape('%0%u007b0')")] + [InlineData("%0{0", "unescape('%0%u007B0')")] + [InlineData("%0" + "\ufffe" + "0", "unescape('%0%ufffe0')")] + [InlineData("%0" + "\ufffe" + "0", "unescape('%0%uFffe0')")] + [InlineData("%0" + "\ufffe" + "0", "unescape('%0%ufFfe0')")] + [InlineData("%0" + "\ufffe" + "0", "unescape('%0%uffFe0')")] + [InlineData("%0" + "\ufffe" + "0", "unescape('%0%ufffE0')")] + [InlineData("%0" + "\ufffe" + "0", "unescape('%0%uFFFE0')")] + [InlineData("%0" + "\uffff" + "0", "unescape('%0%uffff0')")] + [InlineData("%0" + "\uffff" + "0", "unescape('%0%uFfff0')")] + [InlineData("%0" + "\uffff" + "0", "unescape('%0%ufFff0')")] + [InlineData("%0" + "\uffff" + "0", "unescape('%0%uffFf0')")] + [InlineData("%0" + "\uffff" + "0", "unescape('%0%ufffF0')")] + [InlineData("%0" + "\uffff" + "0", "unescape('%0%uFFFF0')")] + //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/two-ignore-end-str.js + [InlineData("%", "unescape('%')")] + [InlineData("%0", "unescape('%0')")] + [InlineData("%1", "unescape('%1')")] + [InlineData("%2", "unescape('%2')")] + [InlineData("%3", "unescape('%3')")] + [InlineData("%4", "unescape('%4')")] + [InlineData("%5", "unescape('%5')")] + [InlineData("%6", "unescape('%6')")] + [InlineData("%7", "unescape('%7')")] + [InlineData("%8", "unescape('%8')")] + [InlineData("%9", "unescape('%9')")] + [InlineData("%a", "unescape('%a')")] + [InlineData("%A", "unescape('%A')")] + [InlineData("%b", "unescape('%b')")] + [InlineData("%B", "unescape('%B')")] + [InlineData("%c", "unescape('%c')")] + [InlineData("%C", "unescape('%C')")] + [InlineData("%d", "unescape('%d')")] + [InlineData("%D", "unescape('%D')")] + [InlineData("%e", "unescape('%e')")] + [InlineData("%E", "unescape('%E')")] + [InlineData("%f", "unescape('%f')")] + [InlineData("%F", "unescape('%F')")] + //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/two-ignore-non-hex.js + [InlineData("%0%0", "unescape('%0%0')")] + [InlineData("%0g0", "unescape('%0g0')")] + [InlineData("%0G0", "unescape('%0G0')")] + [InlineData("%g00", "unescape('%g00')")] + [InlineData("%G00", "unescape('%G00')")] + [InlineData("%0u0", "unescape('%0u0')")] + [InlineData("%0U0", "unescape('%0U0')")] + [InlineData("%u00", "unescape('%u00')")] + [InlineData("%U00", "unescape('%U00')")] + //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/two.js + [InlineData("%0" + "\x00" + "00", "unescape('%0%0000')")] + [InlineData("%0" + "\x01" + "00", "unescape('%0%0100')")] + [InlineData("%0)00", "unescape('%0%2900')")] + [InlineData("%0*00", "unescape('%0%2a00')")] + [InlineData("%0*00", "unescape('%0%2A00')")] + [InlineData("%0+00", "unescape('%0%2b00')")] + [InlineData("%0+00", "unescape('%0%2B00')")] + [InlineData("%0,00", "unescape('%0%2c00')")] + [InlineData("%0,00", "unescape('%0%2C00')")] + [InlineData("%0-00", "unescape('%0%2d00')")] + [InlineData("%0-00", "unescape('%0%2D00')")] + [InlineData("%0900", "unescape('%0%3900')")] + [InlineData("%0:00", "unescape('%0%3a00')")] + [InlineData("%0:00", "unescape('%0%3A00')")] + [InlineData("%0?00", "unescape('%0%3f00')")] + [InlineData("%0?00", "unescape('%0%3F00')")] + [InlineData("%0@00", "unescape('%0%4000')")] + [InlineData("%0Z00", "unescape('%0%5a00')")] + [InlineData("%0Z00", "unescape('%0%5A00')")] + [InlineData("%0[00", "unescape('%0%5b00')")] + [InlineData("%0[00", "unescape('%0%5B00')")] + [InlineData("%0^00", "unescape('%0%5e00')")] + [InlineData("%0^00", "unescape('%0%5E00')")] + [InlineData("%0_00", "unescape('%0%5f00')")] + [InlineData("%0_00", "unescape('%0%5F00')")] + [InlineData("%0`00", "unescape('%0%6000')")] + [InlineData("%0a00", "unescape('%0%6100')")] + [InlineData("%0z00", "unescape('%0%7a00')")] + [InlineData("%0z00", "unescape('%0%7A00')")] + [InlineData("%0{00", "unescape('%0%7b00')")] + [InlineData("%0{00", "unescape('%0%7B00')")] + public void ShouldEvaluateUnescape(object expected, string source) + { + var engine = new Engine(); + var result = engine.Evaluate(source).ToObject(); - Assert.Equal(expected, result); - } + Assert.Equal(expected, result); + } - [Theory] - [InlineData("new Date(1969,0,1,19,45,30,500).getHours()", 19)] - [InlineData("new Date(1970,0,1,19,45,30,500).getHours()", 19)] - [InlineData("new Date(1971,0,1,19,45,30,500).getHours()", 19)] - [InlineData("new Date(1969,0,1,19,45,30,500).getMinutes()", 45)] - [InlineData("new Date(1970,0,1,19,45,30,500).getMinutes()", 45)] - [InlineData("new Date(1971,0,1,19,45,30,500).getMinutes()", 45)] - [InlineData("new Date(1969,0,1,19,45,30,500).getSeconds()", 30)] - [InlineData("new Date(1970,0,1,19,45,30,500).getSeconds()", 30)] - [InlineData("new Date(1971,0,1,19,45,30,500).getSeconds()", 30)] - //[InlineData("new Date(1969,0,1,19,45,30,500).getMilliseconds()", 500)] - //[InlineData("new Date(1970,0,1,19,45,30,500).getMilliseconds()", 500)] - //[InlineData("new Date(1971,0,1,19,45,30,500).getMilliseconds()", 500)] - public void ShouldExtractDateParts(string source, double expected) - { - var engine = new Engine(); - var result = engine.Evaluate(source).ToObject(); + [Theory] + [InlineData("new Date(1969,0,1,19,45,30,500).getHours()", 19)] + [InlineData("new Date(1970,0,1,19,45,30,500).getHours()", 19)] + [InlineData("new Date(1971,0,1,19,45,30,500).getHours()", 19)] + [InlineData("new Date(1969,0,1,19,45,30,500).getMinutes()", 45)] + [InlineData("new Date(1970,0,1,19,45,30,500).getMinutes()", 45)] + [InlineData("new Date(1971,0,1,19,45,30,500).getMinutes()", 45)] + [InlineData("new Date(1969,0,1,19,45,30,500).getSeconds()", 30)] + [InlineData("new Date(1970,0,1,19,45,30,500).getSeconds()", 30)] + [InlineData("new Date(1971,0,1,19,45,30,500).getSeconds()", 30)] + //[InlineData("new Date(1969,0,1,19,45,30,500).getMilliseconds()", 500)] + //[InlineData("new Date(1970,0,1,19,45,30,500).getMilliseconds()", 500)] + //[InlineData("new Date(1971,0,1,19,45,30,500).getMilliseconds()", 500)] + public void ShouldExtractDateParts(string source, double expected) + { + var engine = new Engine(); + var result = engine.Evaluate(source).ToObject(); - Assert.Equal(expected, result); - } + Assert.Equal(expected, result); + } - [Theory] - [InlineData("'abc'.padStart(10)", " abc")] - [InlineData("'abc'.padStart(10, \"foo\")", "foofoofabc")] - [InlineData("'abc'.padStart(6, \"123456\")", "123abc")] - [InlineData("'abc'.padStart(8, \"0\")", "00000abc")] - [InlineData("'abc'.padStart(1)", "abc")] - public void ShouldPadStart(string source, object expected) - { - var engine = new Engine(); - var result = engine.Evaluate(source).ToObject(); + [Theory] + [InlineData("'abc'.padStart(10)", " abc")] + [InlineData("'abc'.padStart(10, \"foo\")", "foofoofabc")] + [InlineData("'abc'.padStart(6, \"123456\")", "123abc")] + [InlineData("'abc'.padStart(8, \"0\")", "00000abc")] + [InlineData("'abc'.padStart(1)", "abc")] + public void ShouldPadStart(string source, object expected) + { + var engine = new Engine(); + var result = engine.Evaluate(source).ToObject(); - Assert.Equal(expected, result); - } + Assert.Equal(expected, result); + } - [Theory] - [InlineData("'abc'.padEnd(10)", "abc ")] - [InlineData("'abc'.padEnd(10, \"foo\")", "abcfoofoof")] - [InlineData("'abc'.padEnd(6, \"123456\")", "abc123")] - [InlineData("'abc'.padEnd(1)", "abc")] - public void ShouldPadEnd(string source, object expected) - { - var engine = new Engine(); - var result = engine.Evaluate(source).ToObject(); - Assert.Equal(expected, result); - } + [Theory] + [InlineData("'abc'.padEnd(10)", "abc ")] + [InlineData("'abc'.padEnd(10, \"foo\")", "abcfoofoof")] + [InlineData("'abc'.padEnd(6, \"123456\")", "abc123")] + [InlineData("'abc'.padEnd(1)", "abc")] + public void ShouldPadEnd(string source, object expected) + { + var engine = new Engine(); + var result = engine.Evaluate(source).ToObject(); + Assert.Equal(expected, result); + } - /// - /// Tests for startsWith - tests created from MDN and https://github.com/mathiasbynens/String.prototype.startsWith/blob/master/tests/tests.js - /// - [Theory] - [InlineData("'To be, or not to be, that is the question.'.startsWith('To be')", true)] - [InlineData("'To be, or not to be, that is the question.'.startsWith('not to be')", false)] - [InlineData("'To be, or not to be, that is the question.'.startsWith()", false)] - [InlineData("'To be, or not to be, that is the question.'.startsWith('not to be', 10)", true)] - [InlineData("'undefined'.startsWith()", true)] - [InlineData("'undefined'.startsWith(undefined)", true)] - [InlineData("'undefined'.startsWith(null)", false)] - [InlineData("'null'.startsWith()", false)] - [InlineData("'null'.startsWith(undefined)", false)] - [InlineData("'null'.startsWith(null)", true)] - [InlineData("'abc'.startsWith()", false)] - [InlineData("'abc'.startsWith('')", true)] - [InlineData("'abc'.startsWith('\0')", false)] - [InlineData("'abc'.startsWith('a')", true)] - [InlineData("'abc'.startsWith('b')", false)] - [InlineData("'abc'.startsWith('ab')", true)] - [InlineData("'abc'.startsWith('bc')", false)] - [InlineData("'abc'.startsWith('abc')", true)] - [InlineData("'abc'.startsWith('bcd')", false)] - [InlineData("'abc'.startsWith('abcd')", false)] - [InlineData("'abc'.startsWith('bcde')", false)] - [InlineData("'abc'.startsWith('', 1)", true)] - [InlineData("'abc'.startsWith('\0', 1)", false)] - [InlineData("'abc'.startsWith('a', 1)", false)] - [InlineData("'abc'.startsWith('b', 1)", true)] - [InlineData("'abc'.startsWith('ab', 1)", false)] - [InlineData("'abc'.startsWith('bc', 1)", true)] - [InlineData("'abc'.startsWith('abc', 1)", false)] - [InlineData("'abc'.startsWith('bcd', 1)", false)] - [InlineData("'abc'.startsWith('abcd', 1)", false)] - [InlineData("'abc'.startsWith('bcde', 1)", false)] - public void ShouldStartWith(string source, object expected) - { - var engine = new Engine(); - var result = engine.Evaluate(source).ToObject(); + /// + /// Tests for startsWith - tests created from MDN and https://github.com/mathiasbynens/String.prototype.startsWith/blob/master/tests/tests.js + /// + [Theory] + [InlineData("'To be, or not to be, that is the question.'.startsWith('To be')", true)] + [InlineData("'To be, or not to be, that is the question.'.startsWith('not to be')", false)] + [InlineData("'To be, or not to be, that is the question.'.startsWith()", false)] + [InlineData("'To be, or not to be, that is the question.'.startsWith('not to be', 10)", true)] + [InlineData("'undefined'.startsWith()", true)] + [InlineData("'undefined'.startsWith(undefined)", true)] + [InlineData("'undefined'.startsWith(null)", false)] + [InlineData("'null'.startsWith()", false)] + [InlineData("'null'.startsWith(undefined)", false)] + [InlineData("'null'.startsWith(null)", true)] + [InlineData("'abc'.startsWith()", false)] + [InlineData("'abc'.startsWith('')", true)] + [InlineData("'abc'.startsWith('\0')", false)] + [InlineData("'abc'.startsWith('a')", true)] + [InlineData("'abc'.startsWith('b')", false)] + [InlineData("'abc'.startsWith('ab')", true)] + [InlineData("'abc'.startsWith('bc')", false)] + [InlineData("'abc'.startsWith('abc')", true)] + [InlineData("'abc'.startsWith('bcd')", false)] + [InlineData("'abc'.startsWith('abcd')", false)] + [InlineData("'abc'.startsWith('bcde')", false)] + [InlineData("'abc'.startsWith('', 1)", true)] + [InlineData("'abc'.startsWith('\0', 1)", false)] + [InlineData("'abc'.startsWith('a', 1)", false)] + [InlineData("'abc'.startsWith('b', 1)", true)] + [InlineData("'abc'.startsWith('ab', 1)", false)] + [InlineData("'abc'.startsWith('bc', 1)", true)] + [InlineData("'abc'.startsWith('abc', 1)", false)] + [InlineData("'abc'.startsWith('bcd', 1)", false)] + [InlineData("'abc'.startsWith('abcd', 1)", false)] + [InlineData("'abc'.startsWith('bcde', 1)", false)] + public void ShouldStartWith(string source, object expected) + { + var engine = new Engine(); + var result = engine.Evaluate(source).ToObject(); - Assert.Equal(expected, result); - } + Assert.Equal(expected, result); + } - [Theory] - [InlineData("throw {}", "undefined")] - [InlineData("throw {message:null}", "null")] - [InlineData("throw {message:''}", "")] - [InlineData("throw {message:2}", "2")] - public void ShouldAllowNonStringMessage(string source, string expected) - { - var engine = new Engine(); - var ex = Assert.Throws(() => engine.Execute(source)); - Assert.Equal(expected, ex.Message); - } + [Theory] + [InlineData("throw {}", "undefined")] + [InlineData("throw {message:null}", "null")] + [InlineData("throw {message:''}", "")] + [InlineData("throw {message:2}", "2")] + public void ShouldAllowNonStringMessage(string source, string expected) + { + var engine = new Engine(); + var ex = Assert.Throws(() => engine.Execute(source)); + Assert.Equal(expected, ex.Message); + } - [Theory] - //Months - [InlineData("new Date(2017, 0, 1, 0, 0, 0)", "new Date(2016, 12, 1, 0, 0, 0)")] - [InlineData("new Date(2016, 0, 1, 23, 59, 59)", "new Date(2015, 12, 1, 23, 59, 59)")] - [InlineData("new Date(2013, 0, 1, 0, 0, 0)", "new Date(2012, 12, 1, 0, 0, 0)")] - [InlineData("new Date(2013, 0, 29, 23, 59, 59)", "new Date(2012, 12, 29, 23, 59, 59)")] - [InlineData("new Date(2015, 11, 1, 0, 0, 0)", "new Date(2016, -1, 1, 0, 0, 0)")] - [InlineData("new Date(2014, 11, 1, 23, 59, 59)", "new Date(2015, -1, 1, 23, 59, 59)")] - [InlineData("new Date(2011, 11, 1, 0, 0, 0)", "new Date(2012, -1, 1, 0, 0, 0)")] - [InlineData("new Date(2011, 11, 29, 23, 59, 59)", "new Date(2012, -1, 29, 23, 59, 59)")] - [InlineData("new Date(2015, 1, 1, 0, 0, 0)", "new Date(2016, -11, 1, 0, 0, 0)")] - [InlineData("new Date(2014, 1, 1, 23, 59, 59)", "new Date(2015, -11, 1, 23, 59, 59)")] - [InlineData("new Date(2011, 1, 1, 0, 0, 0)", "new Date(2012, -11, 1, 0, 0, 0)")] - [InlineData("new Date(2011, 2, 1, 23, 59, 59)", "new Date(2012, -11, 29, 23, 59, 59)")] - [InlineData("new Date(2015, 0, 1, 0, 0, 0)", "new Date(2016, -12, 1, 0, 0, 0)")] - [InlineData("new Date(2014, 0, 1, 23, 59, 59)", "new Date(2015, -12, 1, 23, 59, 59)")] - [InlineData("new Date(2011, 0, 1, 0, 0, 0)", "new Date(2012, -12, 1, 0, 0, 0)")] - [InlineData("new Date(2011, 0, 29, 23, 59, 59)", "new Date(2012, -12, 29, 23, 59, 59)")] - [InlineData("new Date(2014, 11, 1, 0, 0, 0)", "new Date(2016, -13, 1, 0, 0, 0)")] - [InlineData("new Date(2013, 11, 1, 23, 59, 59)", "new Date(2015, -13, 1, 23, 59, 59)")] - [InlineData("new Date(2010, 11, 1, 0, 0, 0)", "new Date(2012, -13, 1, 0, 0, 0)")] - [InlineData("new Date(2010, 11, 29, 23, 59, 59)", "new Date(2012, -13, 29, 23, 59, 59)")] - [InlineData("new Date(2013, 11, 1, 0, 0, 0)", "new Date(2016, -25, 1, 0, 0, 0)")] - [InlineData("new Date(2012, 11, 1, 23, 59, 59)", "new Date(2015, -25, 1, 23, 59, 59)")] - [InlineData("new Date(2009, 11, 1, 0, 0, 0)", "new Date(2012, -25, 1, 0, 0, 0)")] - [InlineData("new Date(2009, 11, 29, 23, 59, 59)", "new Date(2012, -25, 29, 23, 59, 59)")] - //Days - [InlineData("new Date(2016, 1, 11, 0, 0, 0)", "new Date(2016, 0, 42, 0, 0, 0)")] - [InlineData("new Date(2016, 0, 11, 23, 59, 59)", "new Date(2015, 11, 42, 23, 59, 59)")] - [InlineData("new Date(2012, 3, 11, 0, 0, 0)", "new Date(2012, 2, 42, 0, 0, 0)")] - [InlineData("new Date(2012, 2, 13, 23, 59, 59)", "new Date(2012, 1, 42, 23, 59, 59)")] - [InlineData("new Date(2015, 11, 31, 0, 0, 0)", "new Date(2016, 0, 0, 0, 0, 0)")] - [InlineData("new Date(2015, 10, 30, 23, 59, 59)", "new Date(2015, 11, 0, 23, 59, 59)")] - [InlineData("new Date(2012, 1, 29, 0, 0, 0)", "new Date(2012, 2, 0, 0, 0, 0)")] - [InlineData("new Date(2012, 0, 31, 23, 59, 59)", "new Date(2012, 1, 0, 23, 59, 59)")] - [InlineData("new Date(2015, 10, 24, 0, 0, 0)", "new Date(2016, 0, -37, 0, 0, 0)")] - [InlineData("new Date(2015, 9, 24, 23, 59, 59)", "new Date(2015, 11, -37, 23, 59, 59)")] - [InlineData("new Date(2012, 0, 23, 0, 0, 0)", "new Date(2012, 2, -37, 0, 0, 0)")] - [InlineData("new Date(2011, 11, 25, 23, 59, 59)", "new Date(2012, 1, -37, 23, 59, 59)")] - //Hours - [InlineData("new Date(2016, 0, 2, 1, 0, 0)", "new Date(2016, 0, 1, 25, 0, 0)")] - [InlineData("new Date(2015, 11, 2, 1, 59, 59)", "new Date(2015, 11, 1, 25, 59, 59)")] - [InlineData("new Date(2012, 2, 2, 1, 0, 0)", "new Date(2012, 2, 1, 25, 0, 0)")] - [InlineData("new Date(2012, 2, 1, 1, 59, 59)", "new Date(2012, 1, 29, 25, 59, 59)")] - [InlineData("new Date(2016, 0, 19, 3, 0, 0)", "new Date(2016, 0, 1, 435, 0, 0)")] - [InlineData("new Date(2015, 11, 19, 3, 59, 59)", "new Date(2015, 11, 1, 435, 59, 59)")] - [InlineData("new Date(2012, 2, 19, 3, 0, 0)", "new Date(2012, 2, 1, 435, 0, 0)")] - [InlineData("new Date(2012, 2, 18, 3, 59, 59)", "new Date(2012, 1, 29, 435, 59, 59)")] - [InlineData("new Date(2015, 11, 31, 23, 0, 0)", "new Date(2016, 0, 1, -1, 0, 0)")] - [InlineData("new Date(2015, 10, 30, 23, 59, 59)", "new Date(2015, 11, 1, -1, 59, 59)")] - [InlineData("new Date(2012, 1, 29, 23, 0, 0)", "new Date(2012, 2, 1, -1, 0, 0)")] - [InlineData("new Date(2012, 1, 28, 23, 59, 59)", "new Date(2012, 1, 29, -1, 59, 59)")] - [InlineData("new Date(2015, 11, 3, 18, 0, 0)", "new Date(2016, 0, 1, -678, 0, 0)")] - [InlineData("new Date(2015, 10, 2, 18, 59, 59)", "new Date(2015, 11, 1, -678, 59, 59)")] - [InlineData("new Date(2012, 1, 1, 18, 0, 0)", "new Date(2012, 2, 1, -678, 0, 0)")] - [InlineData("new Date(2012, 0, 31, 18, 59, 59)", "new Date(2012, 1, 29, -678, 59, 59)")] - // Minutes - [InlineData("new Date(2016, 0, 1, 1, 0, 0)", "new Date(2016, 0, 1, 0, 60, 0)")] - [InlineData("new Date(2015, 11, 2, 0, 0, 59)", "new Date(2015, 11, 1, 23, 60, 59)")] - [InlineData("new Date(2012, 2, 1, 1, 0, 0)", "new Date(2012, 2, 1, 0, 60, 0)")] - [InlineData("new Date(2012, 2, 1, 0, 0, 59)", "new Date(2012, 1, 29, 23, 60, 59)")] - [InlineData("new Date(2015, 11, 31, 23, 59, 0)", "new Date(2016, 0, 1, 0, -1, 0)")] - [InlineData("new Date(2015, 11, 1, 22, 59, 59)", "new Date(2015, 11, 1, 23, -1, 59)")] - [InlineData("new Date(2012, 1, 29, 23, 59, 0)", "new Date(2012, 2, 1, 0, -1, 0)")] - [InlineData("new Date(2012, 1, 29, 22, 59, 59)", "new Date(2012, 1, 29, 23, -1, 59)")] - [InlineData("new Date(2016, 0, 2, 15, 5, 0)", "new Date(2016, 0, 1, 0, 2345, 0)")] - [InlineData("new Date(2015, 11, 3, 14, 5, 59)", "new Date(2015, 11, 1, 23, 2345, 59)")] - [InlineData("new Date(2012, 2, 2, 15, 5, 0)", "new Date(2012, 2, 1, 0, 2345, 0)")] - [InlineData("new Date(2012, 2, 2, 14, 5, 59)", "new Date(2012, 1, 29, 23, 2345, 59)")] - [InlineData("new Date(2015, 11, 25, 18, 24, 0)", "new Date(2016, 0, 1, 0, -8976, 0)")] - [InlineData("new Date(2015, 10, 25, 17, 24, 59)", "new Date(2015, 11, 1, 23, -8976, 59)")] - [InlineData("new Date(2012, 1, 23, 18, 24, 0)", "new Date(2012, 2, 1, 0, -8976, 0)")] - [InlineData("new Date(2012, 1, 23, 17, 24, 59)", "new Date(2012, 1, 29, 23, -8976, 59)")] - // Seconds - [InlineData("new Date(2016, 0, 1, 0, 1, 0)", "new Date(2016, 0, 1, 0, 0, 60)")] - [InlineData("new Date(2015, 11, 2, 0, 0, 0)", "new Date(2015, 11, 1, 23, 59, 60)")] - [InlineData("new Date(2012, 2, 1, 0, 1, 0)", "new Date(2012, 2, 1, 0, 0, 60)")] - [InlineData("new Date(2012, 2, 1, 0, 0, 0)", "new Date(2012, 1, 29, 23, 59, 60)")] - [InlineData("new Date(2015, 11, 31, 23, 59, 59)", "new Date(2016, 0, 1, 0, 0, -1)")] - [InlineData("new Date(2015, 11, 1, 23, 58, 59)", "new Date(2015, 11, 1, 23, 59, -1)")] - [InlineData("new Date(2012, 1, 29, 23, 59, 59)", "new Date(2012, 2, 1, 0, 0, -1)")] - [InlineData("new Date(2012, 1, 29, 23, 58, 59)", "new Date(2012, 1, 29, 23, 59, -1)")] - [InlineData("new Date(2016, 0, 3, 17, 9, 58)", "new Date(2016, 0, 1, 0, 0, 234598)")] - [InlineData("new Date(2015, 11, 4, 17, 8, 58)", "new Date(2015, 11, 1, 23, 59, 234598)")] - [InlineData("new Date(2012, 2, 3, 17, 9, 58)", "new Date(2012, 2, 1, 0, 0, 234598)")] - [InlineData("new Date(2012, 2, 3, 17, 8, 58)", "new Date(2012, 1, 29, 23, 59, 234598)")] - [InlineData("new Date(2015, 11, 21, 14, 39, 15)", "new Date(2016, 0, 1, 0, 0, -897645)")] - [InlineData("new Date(2015, 10, 21, 14, 38, 15)", "new Date(2015, 11, 1, 23, 59, -897645)")] - [InlineData("new Date(2012, 1, 19, 14, 39, 15)", "new Date(2012, 2, 1, 0, 0, -897645)")] - [InlineData("new Date(2012, 1, 19, 14, 38, 15)", "new Date(2012, 1, 29, 23, 59, -897645)")] - public void ShouldSupportDateConsturctorWithArgumentOutOfRange(string expected, string actual) - { - var engine = new Engine(o => o.LocalTimeZone(TimeZoneInfo.Utc)); - var expectedValue = engine.Evaluate(expected).ToObject(); - var actualValue = engine.Evaluate(actual).ToObject(); - Assert.Equal(expectedValue, actualValue); - } + [Theory] + //Months + [InlineData("new Date(2017, 0, 1, 0, 0, 0)", "new Date(2016, 12, 1, 0, 0, 0)")] + [InlineData("new Date(2016, 0, 1, 23, 59, 59)", "new Date(2015, 12, 1, 23, 59, 59)")] + [InlineData("new Date(2013, 0, 1, 0, 0, 0)", "new Date(2012, 12, 1, 0, 0, 0)")] + [InlineData("new Date(2013, 0, 29, 23, 59, 59)", "new Date(2012, 12, 29, 23, 59, 59)")] + [InlineData("new Date(2015, 11, 1, 0, 0, 0)", "new Date(2016, -1, 1, 0, 0, 0)")] + [InlineData("new Date(2014, 11, 1, 23, 59, 59)", "new Date(2015, -1, 1, 23, 59, 59)")] + [InlineData("new Date(2011, 11, 1, 0, 0, 0)", "new Date(2012, -1, 1, 0, 0, 0)")] + [InlineData("new Date(2011, 11, 29, 23, 59, 59)", "new Date(2012, -1, 29, 23, 59, 59)")] + [InlineData("new Date(2015, 1, 1, 0, 0, 0)", "new Date(2016, -11, 1, 0, 0, 0)")] + [InlineData("new Date(2014, 1, 1, 23, 59, 59)", "new Date(2015, -11, 1, 23, 59, 59)")] + [InlineData("new Date(2011, 1, 1, 0, 0, 0)", "new Date(2012, -11, 1, 0, 0, 0)")] + [InlineData("new Date(2011, 2, 1, 23, 59, 59)", "new Date(2012, -11, 29, 23, 59, 59)")] + [InlineData("new Date(2015, 0, 1, 0, 0, 0)", "new Date(2016, -12, 1, 0, 0, 0)")] + [InlineData("new Date(2014, 0, 1, 23, 59, 59)", "new Date(2015, -12, 1, 23, 59, 59)")] + [InlineData("new Date(2011, 0, 1, 0, 0, 0)", "new Date(2012, -12, 1, 0, 0, 0)")] + [InlineData("new Date(2011, 0, 29, 23, 59, 59)", "new Date(2012, -12, 29, 23, 59, 59)")] + [InlineData("new Date(2014, 11, 1, 0, 0, 0)", "new Date(2016, -13, 1, 0, 0, 0)")] + [InlineData("new Date(2013, 11, 1, 23, 59, 59)", "new Date(2015, -13, 1, 23, 59, 59)")] + [InlineData("new Date(2010, 11, 1, 0, 0, 0)", "new Date(2012, -13, 1, 0, 0, 0)")] + [InlineData("new Date(2010, 11, 29, 23, 59, 59)", "new Date(2012, -13, 29, 23, 59, 59)")] + [InlineData("new Date(2013, 11, 1, 0, 0, 0)", "new Date(2016, -25, 1, 0, 0, 0)")] + [InlineData("new Date(2012, 11, 1, 23, 59, 59)", "new Date(2015, -25, 1, 23, 59, 59)")] + [InlineData("new Date(2009, 11, 1, 0, 0, 0)", "new Date(2012, -25, 1, 0, 0, 0)")] + [InlineData("new Date(2009, 11, 29, 23, 59, 59)", "new Date(2012, -25, 29, 23, 59, 59)")] + //Days + [InlineData("new Date(2016, 1, 11, 0, 0, 0)", "new Date(2016, 0, 42, 0, 0, 0)")] + [InlineData("new Date(2016, 0, 11, 23, 59, 59)", "new Date(2015, 11, 42, 23, 59, 59)")] + [InlineData("new Date(2012, 3, 11, 0, 0, 0)", "new Date(2012, 2, 42, 0, 0, 0)")] + [InlineData("new Date(2012, 2, 13, 23, 59, 59)", "new Date(2012, 1, 42, 23, 59, 59)")] + [InlineData("new Date(2015, 11, 31, 0, 0, 0)", "new Date(2016, 0, 0, 0, 0, 0)")] + [InlineData("new Date(2015, 10, 30, 23, 59, 59)", "new Date(2015, 11, 0, 23, 59, 59)")] + [InlineData("new Date(2012, 1, 29, 0, 0, 0)", "new Date(2012, 2, 0, 0, 0, 0)")] + [InlineData("new Date(2012, 0, 31, 23, 59, 59)", "new Date(2012, 1, 0, 23, 59, 59)")] + [InlineData("new Date(2015, 10, 24, 0, 0, 0)", "new Date(2016, 0, -37, 0, 0, 0)")] + [InlineData("new Date(2015, 9, 24, 23, 59, 59)", "new Date(2015, 11, -37, 23, 59, 59)")] + [InlineData("new Date(2012, 0, 23, 0, 0, 0)", "new Date(2012, 2, -37, 0, 0, 0)")] + [InlineData("new Date(2011, 11, 25, 23, 59, 59)", "new Date(2012, 1, -37, 23, 59, 59)")] + //Hours + [InlineData("new Date(2016, 0, 2, 1, 0, 0)", "new Date(2016, 0, 1, 25, 0, 0)")] + [InlineData("new Date(2015, 11, 2, 1, 59, 59)", "new Date(2015, 11, 1, 25, 59, 59)")] + [InlineData("new Date(2012, 2, 2, 1, 0, 0)", "new Date(2012, 2, 1, 25, 0, 0)")] + [InlineData("new Date(2012, 2, 1, 1, 59, 59)", "new Date(2012, 1, 29, 25, 59, 59)")] + [InlineData("new Date(2016, 0, 19, 3, 0, 0)", "new Date(2016, 0, 1, 435, 0, 0)")] + [InlineData("new Date(2015, 11, 19, 3, 59, 59)", "new Date(2015, 11, 1, 435, 59, 59)")] + [InlineData("new Date(2012, 2, 19, 3, 0, 0)", "new Date(2012, 2, 1, 435, 0, 0)")] + [InlineData("new Date(2012, 2, 18, 3, 59, 59)", "new Date(2012, 1, 29, 435, 59, 59)")] + [InlineData("new Date(2015, 11, 31, 23, 0, 0)", "new Date(2016, 0, 1, -1, 0, 0)")] + [InlineData("new Date(2015, 10, 30, 23, 59, 59)", "new Date(2015, 11, 1, -1, 59, 59)")] + [InlineData("new Date(2012, 1, 29, 23, 0, 0)", "new Date(2012, 2, 1, -1, 0, 0)")] + [InlineData("new Date(2012, 1, 28, 23, 59, 59)", "new Date(2012, 1, 29, -1, 59, 59)")] + [InlineData("new Date(2015, 11, 3, 18, 0, 0)", "new Date(2016, 0, 1, -678, 0, 0)")] + [InlineData("new Date(2015, 10, 2, 18, 59, 59)", "new Date(2015, 11, 1, -678, 59, 59)")] + [InlineData("new Date(2012, 1, 1, 18, 0, 0)", "new Date(2012, 2, 1, -678, 0, 0)")] + [InlineData("new Date(2012, 0, 31, 18, 59, 59)", "new Date(2012, 1, 29, -678, 59, 59)")] + // Minutes + [InlineData("new Date(2016, 0, 1, 1, 0, 0)", "new Date(2016, 0, 1, 0, 60, 0)")] + [InlineData("new Date(2015, 11, 2, 0, 0, 59)", "new Date(2015, 11, 1, 23, 60, 59)")] + [InlineData("new Date(2012, 2, 1, 1, 0, 0)", "new Date(2012, 2, 1, 0, 60, 0)")] + [InlineData("new Date(2012, 2, 1, 0, 0, 59)", "new Date(2012, 1, 29, 23, 60, 59)")] + [InlineData("new Date(2015, 11, 31, 23, 59, 0)", "new Date(2016, 0, 1, 0, -1, 0)")] + [InlineData("new Date(2015, 11, 1, 22, 59, 59)", "new Date(2015, 11, 1, 23, -1, 59)")] + [InlineData("new Date(2012, 1, 29, 23, 59, 0)", "new Date(2012, 2, 1, 0, -1, 0)")] + [InlineData("new Date(2012, 1, 29, 22, 59, 59)", "new Date(2012, 1, 29, 23, -1, 59)")] + [InlineData("new Date(2016, 0, 2, 15, 5, 0)", "new Date(2016, 0, 1, 0, 2345, 0)")] + [InlineData("new Date(2015, 11, 3, 14, 5, 59)", "new Date(2015, 11, 1, 23, 2345, 59)")] + [InlineData("new Date(2012, 2, 2, 15, 5, 0)", "new Date(2012, 2, 1, 0, 2345, 0)")] + [InlineData("new Date(2012, 2, 2, 14, 5, 59)", "new Date(2012, 1, 29, 23, 2345, 59)")] + [InlineData("new Date(2015, 11, 25, 18, 24, 0)", "new Date(2016, 0, 1, 0, -8976, 0)")] + [InlineData("new Date(2015, 10, 25, 17, 24, 59)", "new Date(2015, 11, 1, 23, -8976, 59)")] + [InlineData("new Date(2012, 1, 23, 18, 24, 0)", "new Date(2012, 2, 1, 0, -8976, 0)")] + [InlineData("new Date(2012, 1, 23, 17, 24, 59)", "new Date(2012, 1, 29, 23, -8976, 59)")] + // Seconds + [InlineData("new Date(2016, 0, 1, 0, 1, 0)", "new Date(2016, 0, 1, 0, 0, 60)")] + [InlineData("new Date(2015, 11, 2, 0, 0, 0)", "new Date(2015, 11, 1, 23, 59, 60)")] + [InlineData("new Date(2012, 2, 1, 0, 1, 0)", "new Date(2012, 2, 1, 0, 0, 60)")] + [InlineData("new Date(2012, 2, 1, 0, 0, 0)", "new Date(2012, 1, 29, 23, 59, 60)")] + [InlineData("new Date(2015, 11, 31, 23, 59, 59)", "new Date(2016, 0, 1, 0, 0, -1)")] + [InlineData("new Date(2015, 11, 1, 23, 58, 59)", "new Date(2015, 11, 1, 23, 59, -1)")] + [InlineData("new Date(2012, 1, 29, 23, 59, 59)", "new Date(2012, 2, 1, 0, 0, -1)")] + [InlineData("new Date(2012, 1, 29, 23, 58, 59)", "new Date(2012, 1, 29, 23, 59, -1)")] + [InlineData("new Date(2016, 0, 3, 17, 9, 58)", "new Date(2016, 0, 1, 0, 0, 234598)")] + [InlineData("new Date(2015, 11, 4, 17, 8, 58)", "new Date(2015, 11, 1, 23, 59, 234598)")] + [InlineData("new Date(2012, 2, 3, 17, 9, 58)", "new Date(2012, 2, 1, 0, 0, 234598)")] + [InlineData("new Date(2012, 2, 3, 17, 8, 58)", "new Date(2012, 1, 29, 23, 59, 234598)")] + [InlineData("new Date(2015, 11, 21, 14, 39, 15)", "new Date(2016, 0, 1, 0, 0, -897645)")] + [InlineData("new Date(2015, 10, 21, 14, 38, 15)", "new Date(2015, 11, 1, 23, 59, -897645)")] + [InlineData("new Date(2012, 1, 19, 14, 39, 15)", "new Date(2012, 2, 1, 0, 0, -897645)")] + [InlineData("new Date(2012, 1, 19, 14, 38, 15)", "new Date(2012, 1, 29, 23, 59, -897645)")] + public void ShouldSupportDateConsturctorWithArgumentOutOfRange(string expected, string actual) + { + var engine = new Engine(o => o.LocalTimeZone(TimeZoneInfo.Utc)); + var expectedValue = engine.Evaluate(expected).ToObject(); + var actualValue = engine.Evaluate(actual).ToObject(); + Assert.Equal(expectedValue, actualValue); + } - [Fact] - public void ShouldReturnCorrectConcatenatedStrings() - { - RunTest(@" + [Fact] + public void ShouldReturnCorrectConcatenatedStrings() + { + RunTest(@" function concat(x, a, b) { x += a; x += b; return x; }"); - var concat = _engine.GetValue("concat"); - var result = _engine.Invoke(concat, "concat", "well", "done").ToObject() as string; - Assert.Equal("concatwelldone", result); - } + var concat = _engine.GetValue("concat"); + var result = _engine.Invoke(concat, "concat", "well", "done").ToObject() as string; + Assert.Equal("concatwelldone", result); + } - [Fact] - public void ComplexMappingAndReducing() - { - const string program = @" + [Fact] + public void ComplexMappingAndReducing() + { + const string program = @" Object.map = function (o, f, ctx) { ctx = ctx || this; var result = []; @@ -2630,46 +2630,46 @@ function output(x) { }; }; "; - _engine.Execute(program); - var result1 = (ObjectInstance) _engine.Evaluate("output(x1)"); - var result2 = (ObjectInstance) _engine.Evaluate("output(x2)"); + _engine.Execute(program); + var result1 = (ObjectInstance) _engine.Evaluate("output(x1)"); + var result2 = (ObjectInstance) _engine.Evaluate("output(x2)"); - Assert.Equal(9, TypeConverter.ToNumber(result1.Get("TestDictionarySum1"))); - Assert.Equal(9, TypeConverter.ToNumber(result1.Get("TestDictionarySum2"))); - Assert.Equal(9, TypeConverter.ToNumber(result1.Get("TestDictionarySum3"))); + Assert.Equal(9, TypeConverter.ToNumber(result1.Get("TestDictionarySum1"))); + Assert.Equal(9, TypeConverter.ToNumber(result1.Get("TestDictionarySum2"))); + Assert.Equal(9, TypeConverter.ToNumber(result1.Get("TestDictionarySum3"))); - Assert.Equal(3, TypeConverter.ToNumber(result1.Get("TestDictionaryAverage1"))); - Assert.Equal(3, TypeConverter.ToNumber(result1.Get("TestDictionaryAverage2"))); - Assert.Equal(3, TypeConverter.ToNumber(result1.Get("TestDictionaryAverage3"))); + Assert.Equal(3, TypeConverter.ToNumber(result1.Get("TestDictionaryAverage1"))); + Assert.Equal(3, TypeConverter.ToNumber(result1.Get("TestDictionaryAverage2"))); + Assert.Equal(3, TypeConverter.ToNumber(result1.Get("TestDictionaryAverage3"))); - Assert.Equal(3, TypeConverter.ToNumber(result1.Get("TestDictionaryFunc1"))); - Assert.Equal(1, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionary3"))); + Assert.Equal(3, TypeConverter.ToNumber(result1.Get("TestDictionaryFunc1"))); + Assert.Equal(1, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionary3"))); - Assert.Equal(3.5, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionarySum1"))); - Assert.Equal(3.5, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionarySum2"))); - Assert.Equal(3.5, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionaryAverage1"))); - Assert.Equal(3.5, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionaryAverage2"))); + Assert.Equal(3.5, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionarySum1"))); + Assert.Equal(3.5, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionarySum2"))); + Assert.Equal(3.5, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionaryAverage1"))); + Assert.Equal(3.5, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionaryAverage2"))); - Assert.Equal(1, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionaryDirectAccess3"))); + Assert.Equal(1, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionaryDirectAccess3"))); - Assert.Equal(6.7, TypeConverter.ToNumber(result1.Get("TestList1"))); - Assert.Equal(6.7, TypeConverter.ToNumber(result1.Get("TestList2"))); - Assert.Equal(6.7, TypeConverter.ToNumber(result1.Get("TestList3"))); - Assert.Equal(3.35, TypeConverter.ToNumber(result1.Get("TestList4"))); - Assert.Equal(3.35, TypeConverter.ToNumber(result1.Get("TestList5"))); + Assert.Equal(6.7, TypeConverter.ToNumber(result1.Get("TestList1"))); + Assert.Equal(6.7, TypeConverter.ToNumber(result1.Get("TestList2"))); + Assert.Equal(6.7, TypeConverter.ToNumber(result1.Get("TestList3"))); + Assert.Equal(3.35, TypeConverter.ToNumber(result1.Get("TestList4"))); + Assert.Equal(3.35, TypeConverter.ToNumber(result1.Get("TestList5"))); - Assert.Equal(6, TypeConverter.ToNumber(result2.Get("TestDictionarySum1"))); - Assert.Equal(6, TypeConverter.ToNumber(result2.Get("TestDictionarySum2"))); - Assert.Equal(6, TypeConverter.ToNumber(result2.Get("TestDictionarySum3"))); + Assert.Equal(6, TypeConverter.ToNumber(result2.Get("TestDictionarySum1"))); + Assert.Equal(6, TypeConverter.ToNumber(result2.Get("TestDictionarySum2"))); + Assert.Equal(6, TypeConverter.ToNumber(result2.Get("TestDictionarySum3"))); - Assert.Equal(2, TypeConverter.ToNumber(result2.Get("TestDictionaryAverage1"))); - Assert.Equal(2, TypeConverter.ToNumber(result2.Get("TestDictionaryAverage2"))); - Assert.Equal(2, TypeConverter.ToNumber(result2.Get("TestDictionaryAverage3"))); - } - [Fact] - public void ShouldBeAbleToSpreadArrayLiteralsAndFunctionParameters() - { - RunTest(@" + Assert.Equal(2, TypeConverter.ToNumber(result2.Get("TestDictionaryAverage1"))); + Assert.Equal(2, TypeConverter.ToNumber(result2.Get("TestDictionaryAverage2"))); + Assert.Equal(2, TypeConverter.ToNumber(result2.Get("TestDictionaryAverage3"))); + } + [Fact] + public void ShouldBeAbleToSpreadArrayLiteralsAndFunctionParameters() + { + RunTest(@" function concat(x, a, b) { x += a; x += b; @@ -2682,138 +2682,138 @@ function concat(x, a, b) { var r = [...arr2, ...arr1]; "); - var arrayInstance = (ArrayInstance) _engine.GetValue("r"); - Assert.Equal(arrayInstance[0], 3); - Assert.Equal(arrayInstance[1], 4); - Assert.Equal(arrayInstance[2], 1); - Assert.Equal(arrayInstance[3], 2); + var arrayInstance = (ArrayInstance) _engine.GetValue("r"); + Assert.Equal(arrayInstance[0], 3); + Assert.Equal(arrayInstance[1], 4); + Assert.Equal(arrayInstance[2], 1); + Assert.Equal(arrayInstance[3], 2); - arrayInstance = (ArrayInstance) _engine.GetValue("s"); - Assert.Equal(arrayInstance[0], 'a'); - Assert.Equal(arrayInstance[1], 'b'); - Assert.Equal(arrayInstance[2], 'c'); + arrayInstance = (ArrayInstance) _engine.GetValue("s"); + Assert.Equal(arrayInstance[0], 'a'); + Assert.Equal(arrayInstance[1], 'b'); + Assert.Equal(arrayInstance[2], 'c'); - var c = _engine.GetValue("c").ToString(); - Assert.Equal("1ab", c); - } + var c = _engine.GetValue("c").ToString(); + Assert.Equal("1ab", c); + } - [Fact] - public void ShouldSupportDefaultsInFunctionParameters() - { - RunTest(@" + [Fact] + public void ShouldSupportDefaultsInFunctionParameters() + { + RunTest(@" function f(x, y=12) { // y is 12 if not passed (or passed as undefined) return x + y; } "); - var function = _engine.GetValue("f"); - var result = _engine.Invoke(function, 3).ToString(); - Assert.Equal("15", result); + var function = _engine.GetValue("f"); + var result = _engine.Invoke(function, 3).ToString(); + Assert.Equal("15", result); - result = _engine.Invoke(function, 3, JsValue.Undefined).ToString(); - Assert.Equal("15", result); - } + result = _engine.Invoke(function, 3, JsValue.Undefined).ToString(); + Assert.Equal("15", result); + } - [Fact] - public void ShouldReportErrorForInvalidJson() - { - var engine = new Engine(); - var ex = Assert.Throws(() => engine.Evaluate("JSON.parse('[01]')")); - Assert.Equal("Unexpected token '1' in JSON at position 2", ex.Message); + [Fact] + public void ShouldReportErrorForInvalidJson() + { + var engine = new Engine(); + var ex = Assert.Throws(() => engine.Evaluate("JSON.parse('[01]')")); + Assert.Equal("Unexpected token '1' in JSON at position 2", ex.Message); - var voidCompletion = engine.Evaluate("try { JSON.parse('01') } catch (e) {}"); - Assert.Equal(JsValue.Undefined, voidCompletion); - } + var voidCompletion = engine.Evaluate("try { JSON.parse('01') } catch (e) {}"); + Assert.Equal(JsValue.Undefined, voidCompletion); + } - [Fact] - public void ShouldParseAnonymousToTypeObject() - { - var obj = new Wrapper(); - var engine = new Engine() - .SetValue("x", obj); - var js = @" + [Fact] + public void ShouldParseAnonymousToTypeObject() + { + var obj = new Wrapper(); + var engine = new Engine() + .SetValue("x", obj); + var js = @" x.test = { name: 'Testificate', init (a, b) { return a + b } }"; - engine.Execute(js); + engine.Execute(js); - Assert.Equal("Testificate", obj.Test.Name); - Assert.Equal(5, obj.Test.Init(2, 3)); - } + Assert.Equal("Testificate", obj.Test.Name); + Assert.Equal(5, obj.Test.Init(2, 3)); + } - [Fact] - public void ShouldOverrideDefaultTypeConverter() - { - var engine = new Engine(options => options - .SetTypeConverter(e => new TestTypeConverter()) - ); - Assert.IsType(engine.TypeConverter); - engine.SetValue("x", new Testificate()); - Assert.Throws(() => engine.Evaluate("c.Name")); - } + [Fact] + public void ShouldOverrideDefaultTypeConverter() + { + var engine = new Engine(options => options + .SetTypeConverter(e => new TestTypeConverter()) + ); + Assert.IsType(engine.TypeConverter); + engine.SetValue("x", new Testificate()); + Assert.Throws(() => engine.Evaluate("c.Name")); + } - [Fact] - public void ShouldAllowDollarPrefixForProperties() - { - _engine.SetValue("str", "Hello"); - _engine.Evaluate("equal(undefined, str.$ref);"); - _engine.Evaluate("equal(undefined, str.ref);"); - _engine.Evaluate("equal(undefined, str.$foo);"); - _engine.Evaluate("equal(undefined, str.foo);"); - _engine.Evaluate("equal(undefined, str['$foo']);"); - _engine.Evaluate("equal(undefined, str['foo']);"); - - _engine.Evaluate("equal(false, str.hasOwnProperty('$foo'));"); - _engine.Evaluate("equal(false, str.hasOwnProperty('foo'));"); - } + [Fact] + public void ShouldAllowDollarPrefixForProperties() + { + _engine.SetValue("str", "Hello"); + _engine.Evaluate("equal(undefined, str.$ref);"); + _engine.Evaluate("equal(undefined, str.ref);"); + _engine.Evaluate("equal(undefined, str.$foo);"); + _engine.Evaluate("equal(undefined, str.foo);"); + _engine.Evaluate("equal(undefined, str['$foo']);"); + _engine.Evaluate("equal(undefined, str['foo']);"); + + _engine.Evaluate("equal(false, str.hasOwnProperty('$foo'));"); + _engine.Evaluate("equal(false, str.hasOwnProperty('foo'));"); + } - [Fact] - public void ShouldProvideEngineForOptionsAsOverload() - { - new Engine((e, options) => - { - Assert.IsType(e); - options - .AddObjectConverter(new TestObjectConverter()) - .AddObjectConverter(); - }) - .SetValue("a", 1); - } + [Fact] + public void ShouldProvideEngineForOptionsAsOverload() + { + new Engine((e, options) => + { + Assert.IsType(e); + options + .AddObjectConverter(new TestObjectConverter()) + .AddObjectConverter(); + }) + .SetValue("a", 1); + } - [Fact] - public void ShouldReuseOptions() - { - var options = new Options().Configure(e => e.SetValue("x", 1)); + [Fact] + public void ShouldReuseOptions() + { + var options = new Options().Configure(e => e.SetValue("x", 1)); - var engine1 = new Engine(options); - var engine2 = new Engine(options); + var engine1 = new Engine(options); + var engine2 = new Engine(options); - Assert.Equal(1, Convert.ToInt32(engine1.GetValue("x").ToObject())); - Assert.Equal(1, Convert.ToInt32(engine2.GetValue("x").ToObject())); - } + Assert.Equal(1, Convert.ToInt32(engine1.GetValue("x").ToObject())); + Assert.Equal(1, Convert.ToInt32(engine2.GetValue("x").ToObject())); + } - [Fact] - public void RecursiveCallStack() - { - var engine = new Engine(); - Func evaluateCode = code => engine.Evaluate(code); - var evaluateCodeValue = JsValue.FromObject(engine, evaluateCode); + [Fact] + public void RecursiveCallStack() + { + var engine = new Engine(); + Func evaluateCode = code => engine.Evaluate(code); + var evaluateCodeValue = JsValue.FromObject(engine, evaluateCode); - engine.SetValue("evaluateCode", evaluateCodeValue); - var result = (int) engine.Evaluate(@"evaluateCode('678 + 711')").AsNumber(); + engine.SetValue("evaluateCode", evaluateCodeValue); + var result = (int) engine.Evaluate(@"evaluateCode('678 + 711')").AsNumber(); - Assert.Equal(1389, result); - } + Assert.Equal(1389, result); + } - [Fact] - public void MemberExpressionInObjectProperty() - { - var engine = new Engine(); - dynamic result = engine.Evaluate(@" + [Fact] + public void MemberExpressionInObjectProperty() + { + var engine = new Engine(); + dynamic result = engine.Evaluate(@" const colorMap = { Red: ""red"", Orange: ""orange"", @@ -2827,276 +2827,275 @@ public void MemberExpressionInObjectProperty() }, {}); ") - .ToObject(); + .ToObject(); - Assert.Equal("Red", result.red); - Assert.Equal("Orange", result.orange); - Assert.Equal("White", result.white); - } + Assert.Equal("Red", result.red); + Assert.Equal("Orange", result.orange); + Assert.Equal("White", result.white); + } - [Fact] - public void TypeofShouldEvaluateOnce() - { - var engine = new Engine(); - var result = engine.Evaluate(@" + [Fact] + public void TypeofShouldEvaluateOnce() + { + var engine = new Engine(); + var result = engine.Evaluate(@" let res = 0; const fn = () => res++; typeof fn(); res; ") - .AsNumber(); + .AsNumber(); - Assert.Equal(1, result); - } + Assert.Equal(1, result); + } - [Fact] - public void ClassDeclarationHoisting() - { - var ex = Assert.Throws(() => _engine.Evaluate("typeof MyClass; class MyClass {}")); - Assert.Equal("Cannot access 'MyClass' before initialization", ex.Message); - } + [Fact] + public void ClassDeclarationHoisting() + { + var ex = Assert.Throws(() => _engine.Evaluate("typeof MyClass; class MyClass {}")); + Assert.Equal("Cannot access 'MyClass' before initialization", ex.Message); + } - [Fact] - public void ShouldObeyScriptLevelStrictModeInFunctions() - { - var engine = new Engine(); - const string source = "'use strict'; var x = () => { delete Boolean.prototype; }; x();"; - var ex = Assert.Throws(() => engine.Evaluate(source)); - Assert.Equal("Cannot delete property 'prototype' of function Boolean() { [native code] }", ex.Message); - - const string source2 = "'use strict'; delete foobar;"; - ex = Assert.Throws(() => engine.Evaluate(source2)); - Assert.Equal("Delete of an unqualified identifier in strict mode (:1:22)", ex.Message); - } + [Fact] + public void ShouldObeyScriptLevelStrictModeInFunctions() + { + var engine = new Engine(); + const string source = "'use strict'; var x = () => { delete Boolean.prototype; }; x();"; + var ex = Assert.Throws(() => engine.Evaluate(source)); + Assert.Equal("Cannot delete property 'prototype' of function Boolean() { [native code] }", ex.Message); + + const string source2 = "'use strict'; delete foobar;"; + ex = Assert.Throws(() => engine.Evaluate(source2)); + Assert.Equal("Delete of an unqualified identifier in strict mode (:1:22)", ex.Message); + } - [Fact] - public void ShouldSupportThisInSubclass() - { - var engine = new Engine(); - var script = "class MyClass1 { } class MyClass2 extends MyClass1 { constructor() { } } const x = new MyClass2();"; + [Fact] + public void ShouldSupportThisInSubclass() + { + var engine = new Engine(); + var script = "class MyClass1 { } class MyClass2 extends MyClass1 { constructor() { } } const x = new MyClass2();"; - var ex = Assert.Throws(() => engine.Evaluate(script)); - Assert.Equal("Must call super constructor in derived class before accessing 'this' or returning from derived constructor", ex.Message); - } + var ex = Assert.Throws(() => engine.Evaluate(script)); + Assert.Equal("Must call super constructor in derived class before accessing 'this' or returning from derived constructor", ex.Message); + } - [Fact] - public void ShouldGetZeroPrefixedNumericKeys() - { - var engine = new Engine(); - engine.Evaluate("const testObj = { '02100' : true };"); - Assert.Equal(1, engine.Evaluate("Object.keys(testObj).length;").AsNumber()); - Assert.Equal("[\"02100\"]", engine.Evaluate("JSON.stringify(Object.getOwnPropertyNames(testObj));").AsString()); - } + [Fact] + public void ShouldGetZeroPrefixedNumericKeys() + { + var engine = new Engine(); + engine.Evaluate("const testObj = { '02100' : true };"); + Assert.Equal(1, engine.Evaluate("Object.keys(testObj).length;").AsNumber()); + Assert.Equal("[\"02100\"]", engine.Evaluate("JSON.stringify(Object.getOwnPropertyNames(testObj));").AsString()); + } - [Fact] - public void ShouldAllowOptionalChainingForMemberCall() - { - var engine = new Engine(); - const string Script = @" + [Fact] + public void ShouldAllowOptionalChainingForMemberCall() + { + var engine = new Engine(); + const string Script = @" const adventurer = { name: 'Alice', cat: { name: 'Dinah' } }; const dogName = adventurer.dog?.name; const methodResult = adventurer.someNonExistentMethod?.(); return [ dogName, methodResult ]; "; - var array = engine.Evaluate(Script).AsArray(); + var array = engine.Evaluate(Script).AsArray(); - Assert.Equal(2L, array.Length); - Assert.True(array[0].IsUndefined()); - Assert.True(array[1].IsUndefined()); - } + Assert.Equal(2L, array.Length); + Assert.True(array[0].IsUndefined()); + Assert.True(array[1].IsUndefined()); + } - [Fact] - public void CanDisableCompilation() + [Fact] + public void CanDisableCompilation() + { + var engine = new Engine(options => { - var engine = new Engine(options => - { - options.DisableStringCompilation(); - }); + options.DisableStringCompilation(); + }); - const string ExpectedExceptionMessage = "String compilation has been disabled in engine options"; + const string ExpectedExceptionMessage = "String compilation has been disabled in engine options"; - var ex = Assert.Throws(() => engine.Evaluate("eval('1+1');")); - Assert.Equal(ExpectedExceptionMessage, ex.Message); + var ex = Assert.Throws(() => engine.Evaluate("eval('1+1');")); + Assert.Equal(ExpectedExceptionMessage, ex.Message); - ex = Assert.Throws(() => engine.Evaluate("new Function('1+1');")); - Assert.Equal(ExpectedExceptionMessage, ex.Message); - } + ex = Assert.Throws(() => engine.Evaluate("new Function('1+1');")); + Assert.Equal(ExpectedExceptionMessage, ex.Message); + } - [Fact] - public void ExecuteShouldTriggerBeforeEvaluateEvent() - { - TestBeforeEvaluateEvent( - (engine, code) => engine.Execute(code), - expectedSource: "" - ); - } + [Fact] + public void ExecuteShouldTriggerBeforeEvaluateEvent() + { + TestBeforeEvaluateEvent( + (engine, code) => engine.Execute(code), + expectedSource: "" + ); + } - [Fact] - public void ExecuteWithSourceShouldTriggerBeforeEvaluateEvent() - { - TestBeforeEvaluateEvent( - (engine, code) => engine.Execute(code, "mysource"), - expectedSource: "mysource" - ); - } + [Fact] + public void ExecuteWithSourceShouldTriggerBeforeEvaluateEvent() + { + TestBeforeEvaluateEvent( + (engine, code) => engine.Execute(code, "mysource"), + expectedSource: "mysource" + ); + } - [Fact] - public void ExecuteWithParserOptionsShouldTriggerBeforeEvaluateEvent() - { - TestBeforeEvaluateEvent( - (engine, code) => engine.Execute(code, ScriptParsingOptions.Default), - expectedSource: "" - ); - } + [Fact] + public void ExecuteWithParserOptionsShouldTriggerBeforeEvaluateEvent() + { + TestBeforeEvaluateEvent( + (engine, code) => engine.Execute(code, ScriptParsingOptions.Default), + expectedSource: "" + ); + } - [Fact] - public void ExecuteWithSourceAndParserOptionsShouldTriggerBeforeEvaluateEvent() - { - TestBeforeEvaluateEvent( - (engine, code) => engine.Execute(code, "mysource", ScriptParsingOptions.Default), - expectedSource: "mysource" - ); - } + [Fact] + public void ExecuteWithSourceAndParserOptionsShouldTriggerBeforeEvaluateEvent() + { + TestBeforeEvaluateEvent( + (engine, code) => engine.Execute(code, "mysource", ScriptParsingOptions.Default), + expectedSource: "mysource" + ); + } - [Fact] - public void EvaluateShouldTriggerBeforeEvaluateEvent() - { - TestBeforeEvaluateEvent( - (engine, code) => engine.Evaluate(code), - expectedSource: "" - ); - } + [Fact] + public void EvaluateShouldTriggerBeforeEvaluateEvent() + { + TestBeforeEvaluateEvent( + (engine, code) => engine.Evaluate(code), + expectedSource: "" + ); + } - [Fact] - public void EvaluateWithSourceShouldTriggerBeforeEvaluateEvent() - { - TestBeforeEvaluateEvent( - (engine, code) => engine.Evaluate(code, "mysource"), - expectedSource: "mysource" - ); - } + [Fact] + public void EvaluateWithSourceShouldTriggerBeforeEvaluateEvent() + { + TestBeforeEvaluateEvent( + (engine, code) => engine.Evaluate(code, "mysource"), + expectedSource: "mysource" + ); + } - [Fact] - public void EvaluateWithParserOptionsShouldTriggerBeforeEvaluateEvent() - { - TestBeforeEvaluateEvent( - (engine, code) => engine.Evaluate(code, ScriptParsingOptions.Default), - expectedSource: "" - ); - } + [Fact] + public void EvaluateWithParserOptionsShouldTriggerBeforeEvaluateEvent() + { + TestBeforeEvaluateEvent( + (engine, code) => engine.Evaluate(code, ScriptParsingOptions.Default), + expectedSource: "" + ); + } - [Fact] - public void EvaluateWithSourceAndParserOptionsShouldTriggerBeforeEvaluateEvent() - { - TestBeforeEvaluateEvent( - (engine, code) => engine.Evaluate(code, "mysource", ScriptParsingOptions.Default), - expectedSource: "mysource" - ); - } + [Fact] + public void EvaluateWithSourceAndParserOptionsShouldTriggerBeforeEvaluateEvent() + { + TestBeforeEvaluateEvent( + (engine, code) => engine.Evaluate(code, "mysource", ScriptParsingOptions.Default), + expectedSource: "mysource" + ); + } - [Fact] - public void ImportModuleShouldTriggerBeforeEvaluateEvents() - { - var engine = new Engine(); + [Fact] + public void ImportModuleShouldTriggerBeforeEvaluateEvents() + { + var engine = new Engine(); - const string module1 = "import dummy from 'module2';"; - const string module2 = "export default 'dummy';"; + const string module1 = "import dummy from 'module2';"; + const string module2 = "export default 'dummy';"; - var beforeEvaluateTriggeredCount = 0; - engine.Debugger.BeforeEvaluate += (sender, ast) => - { - beforeEvaluateTriggeredCount++; - Assert.Equal(engine, sender); + var beforeEvaluateTriggeredCount = 0; + engine.Debugger.BeforeEvaluate += (sender, ast) => + { + beforeEvaluateTriggeredCount++; + Assert.Equal(engine, sender); - switch (beforeEvaluateTriggeredCount) - { - case 1: - Assert.Equal("module1", ast.Location.SourceFile); - Assert.Collection(ast.Body, - node => Assert.IsType(node) - ); - break; - case 2: - Assert.Equal("module2", ast.Location.SourceFile); - Assert.Collection(ast.Body, - node => Assert.IsType(node) - ); - break; - } - }; + switch (beforeEvaluateTriggeredCount) + { + case 1: + Assert.Equal("module1", ast.Location.SourceFile); + Assert.Collection(ast.Body, + node => Assert.IsType(node) + ); + break; + case 2: + Assert.Equal("module2", ast.Location.SourceFile); + Assert.Collection(ast.Body, + node => Assert.IsType(node) + ); + break; + } + }; - engine.Modules.Add("module1", module1); - engine.Modules.Add("module2", module2); - engine.Modules.Import("module1"); + engine.Modules.Add("module1", module1); + engine.Modules.Add("module2", module2); + engine.Modules.Import("module1"); - Assert.Equal(2, beforeEvaluateTriggeredCount); - } + Assert.Equal(2, beforeEvaluateTriggeredCount); + } - [Fact] - public void ShouldConvertJsTypedArraysCorrectly() - { - var engine = new Engine(); + [Fact] + public void ShouldConvertJsTypedArraysCorrectly() + { + var engine = new Engine(); - var float32 = new float [] { 42f, 23 }; + var float32 = new float [] { 42f, 23 }; - engine.SetValue("float32", float32); - engine.SetValue("testFloat32Array", new Action(v => Assert.Equal(v, float32))); + engine.SetValue("float32", float32); + engine.SetValue("testFloat32Array", new Action(v => Assert.Equal(v, float32))); - engine.Evaluate(@" + engine.Evaluate(@" testFloat32Array(new Float32Array(float32)); "); - } + } - private static void TestBeforeEvaluateEvent(Action call, string expectedSource) - { - var engine = new Engine(); + private static void TestBeforeEvaluateEvent(Action call, string expectedSource) + { + var engine = new Engine(); - const string script = "'dummy';"; + const string script = "'dummy';"; - var beforeEvaluateTriggered = false; - engine.Debugger.BeforeEvaluate += (sender, ast) => - { - beforeEvaluateTriggered = true; - Assert.Equal(engine, sender); - Assert.Equal(expectedSource, ast.Location.SourceFile); - Assert.Collection(ast.Body, node => Assert.True(TestHelpers.IsLiteral(node, "dummy"))); - }; + var beforeEvaluateTriggered = false; + engine.Debugger.BeforeEvaluate += (sender, ast) => + { + beforeEvaluateTriggered = true; + Assert.Equal(engine, sender); + Assert.Equal(expectedSource, ast.Location.SourceFile); + Assert.Collection(ast.Body, node => Assert.True(TestHelpers.IsLiteral(node, "dummy"))); + }; - call(engine, script); + call(engine, script); - Assert.True(beforeEvaluateTriggered); - } + Assert.True(beforeEvaluateTriggered); + } - private class Wrapper - { - public Testificate Test { get; set; } - } + private class Wrapper + { + public Testificate Test { get; set; } + } - private class Testificate + private class Testificate + { + public string Name { get; set; } + public Func Init { get; set; } + } + + private class TestObjectConverter : Jint.Runtime.Interop.IObjectConverter + { + public bool TryConvert(Engine engine, object value, out JsValue result) { - public string Name { get; set; } - public Func Init { get; set; } + throw new NotImplementedException(); } + } - private class TestObjectConverter : Jint.Runtime.Interop.IObjectConverter + private class TestTypeConverter : Jint.Runtime.Interop.ITypeConverter + { + public object Convert(object value, Type type, IFormatProvider formatProvider) { - public bool TryConvert(Engine engine, object value, out JsValue result) - { - throw new NotImplementedException(); - } + throw new NotImplementedException(); } - private class TestTypeConverter : Jint.Runtime.Interop.ITypeConverter + public bool TryConvert(object value, Type type, IFormatProvider formatProvider, out object converted) { - public object Convert(object value, Type type, IFormatProvider formatProvider) - { - throw new NotImplementedException(); - } - - public bool TryConvert(object value, Type type, IFormatProvider formatProvider, out object converted) - { - throw new NotImplementedException(); - } + throw new NotImplementedException(); } } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/ErrorTests.cs b/Jint.Tests/Runtime/ErrorTests.cs index 8920a0fa7e..3df86be76a 100644 --- a/Jint.Tests/Runtime/ErrorTests.cs +++ b/Jint.Tests/Runtime/ErrorTests.cs @@ -3,62 +3,62 @@ using Jint.Runtime; using Jint.Tests.Runtime.TestClasses; -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class ErrorTests { - public class ErrorTests + [Fact] + public void CanReturnCorrectErrorMessageAndLocation1() { - [Fact] - public void CanReturnCorrectErrorMessageAndLocation1() - { - const string script = @" + const string script = @" var a = {}; var b = a.user.name; "; - var engine = new Engine(); - var e = Assert.Throws(() => engine.Execute(script)); - Assert.Equal("Cannot read property 'name' of undefined", e.Message); - Assert.Equal(4, e.Location.Start.Line); - Assert.Equal(15, e.Location.Start.Column); - } + var engine = new Engine(); + var e = Assert.Throws(() => engine.Execute(script)); + Assert.Equal("Cannot read property 'name' of undefined", e.Message); + Assert.Equal(4, e.Location.Start.Line); + Assert.Equal(15, e.Location.Start.Column); + } - [Fact] - public void CanReturnCorrectErrorMessageAndLocation1WithoutReferencedName() - { - const string script = @" + [Fact] + public void CanReturnCorrectErrorMessageAndLocation1WithoutReferencedName() + { + const string script = @" var c = a(b().Length); "; - var engine = new Engine(); - engine.SetValue("a", new Action((_) => { })); - engine.SetValue("b", new Func(() => null)); - var e = Assert.Throws(() => engine.Execute(script)); - Assert.Equal("Cannot read property 'Length' of null", e.Message); - Assert.Equal(2, e.Location.Start.Line); - Assert.Equal(14, e.Location.Start.Column); - } + var engine = new Engine(); + engine.SetValue("a", new Action((_) => { })); + engine.SetValue("b", new Func(() => null)); + var e = Assert.Throws(() => engine.Execute(script)); + Assert.Equal("Cannot read property 'Length' of null", e.Message); + Assert.Equal(2, e.Location.Start.Line); + Assert.Equal(14, e.Location.Start.Column); + } - [Fact] - public void CanReturnCorrectErrorMessageAndLocation2() - { - const string script = @" + [Fact] + public void CanReturnCorrectErrorMessageAndLocation2() + { + const string script = @" test(); "; - var engine = new Engine(); - var e = Assert.Throws(() => engine.Execute(script)); - Assert.Equal("test is not defined", e.Message); - Assert.Equal(2, e.Location.Start.Line); - Assert.Equal(1, e.Location.Start.Column); - } + var engine = new Engine(); + var e = Assert.Throws(() => engine.Execute(script)); + Assert.Equal("test is not defined", e.Message); + Assert.Equal(2, e.Location.Start.Line); + Assert.Equal(1, e.Location.Start.Column); + } - [Fact] - public void CanProduceCorrectStackTraceForInternalError() - { - var engine = new Engine(); + [Fact] + public void CanProduceCorrectStackTraceForInternalError() + { + var engine = new Engine(); - engine.Execute(@" + engine.Execute(@" var a = function(v) { return v.xxx.yyy; } @@ -68,24 +68,24 @@ public void CanProduceCorrectStackTraceForInternalError() } ", "custom.js"); - var e = Assert.Throws(() => engine.Execute("var x = b(7);", "main.js")); - Assert.Equal("Cannot read property 'yyy' of undefined", e.Message); - Assert.Equal(3, e.Location.Start.Line); - Assert.Equal(15, e.Location.Start.Column); - Assert.Equal("custom.js", e.Location.SourceFile); + var e = Assert.Throws(() => engine.Execute("var x = b(7);", "main.js")); + Assert.Equal("Cannot read property 'yyy' of undefined", e.Message); + Assert.Equal(3, e.Location.Start.Line); + Assert.Equal(15, e.Location.Start.Column); + Assert.Equal("custom.js", e.Location.SourceFile); - var stack = e.JavaScriptStackTrace; - EqualIgnoringNewLineDifferences(@" at a (v) custom.js:3:16 + var stack = e.JavaScriptStackTrace; + EqualIgnoringNewLineDifferences(@" at a (v) custom.js:3:16 at b (v) custom.js:7:10 at main.js:1:9", stack); - } + } - [Fact] - public void CanProduceCorrectStackTraceForScriptError() - { - var engine = new Engine(); + [Fact] + public void CanProduceCorrectStackTraceForScriptError() + { + var engine = new Engine(); - engine.Execute(@" + engine.Execute(@" var a = function(v) { throw new Error('Error thrown from script'); } @@ -95,24 +95,24 @@ public void CanProduceCorrectStackTraceForScriptError() } ", "custom.js"); - var e = Assert.Throws(() => engine.Execute("var x = b(7);", "main.js")); - Assert.Equal("Error thrown from script", e.Message); - Assert.Equal(3, e.Location.Start.Line); - Assert.Equal(8, e.Location.Start.Column); - Assert.Equal("custom.js", e.Location.SourceFile); + var e = Assert.Throws(() => engine.Execute("var x = b(7);", "main.js")); + Assert.Equal("Error thrown from script", e.Message); + Assert.Equal(3, e.Location.Start.Line); + Assert.Equal(8, e.Location.Start.Column); + Assert.Equal("custom.js", e.Location.SourceFile); - var stack = e.JavaScriptStackTrace; - EqualIgnoringNewLineDifferences(@" at a (v) custom.js:3:9 + var stack = e.JavaScriptStackTrace; + EqualIgnoringNewLineDifferences(@" at a (v) custom.js:3:9 at b (v) custom.js:7:10 at main.js:1:9", stack); - } + } - [Fact] - public void ErrorObjectHasTheStackTraceImmediately() - { - var engine = new Engine(); + [Fact] + public void ErrorObjectHasTheStackTraceImmediately() + { + var engine = new Engine(); - engine.Execute(@" + engine.Execute(@" var a = function(v) { return Error().stack; } @@ -122,20 +122,20 @@ public void ErrorObjectHasTheStackTraceImmediately() } ", "custom.js"); - var e = engine.Evaluate(@"b(7)", "main.js").AsString(); + var e = engine.Evaluate(@"b(7)", "main.js").AsString(); - var stack = e; - EqualIgnoringNewLineDifferences(@" at a (v) custom.js:3:10 + var stack = e; + EqualIgnoringNewLineDifferences(@" at a (v) custom.js:3:10 at b (v) custom.js:7:10 at main.js:1:1", stack); - } + } - [Fact] - public void ThrownErrorObjectHasStackTraceInCatch() - { - var engine = new Engine(); + [Fact] + public void ThrownErrorObjectHasStackTraceInCatch() + { + var engine = new Engine(); - engine.Execute(@" + engine.Execute(@" var a = function(v) { try { throw Error(); @@ -149,21 +149,21 @@ public void ThrownErrorObjectHasStackTraceInCatch() } ", "custom.js"); - var e = engine.Evaluate(@"b(7)", "main.js").AsString(); + var e = engine.Evaluate(@"b(7)", "main.js").AsString(); - var stack = e; - EqualIgnoringNewLineDifferences(@" at a (v) custom.js:4:11 + var stack = e; + EqualIgnoringNewLineDifferences(@" at a (v) custom.js:4:11 at b (v) custom.js:11:10 at main.js:1:1", stack); - } + } - [Fact] - public void GeneratedErrorHasStackTraceInCatch() - { - var engine = new Engine(); + [Fact] + public void GeneratedErrorHasStackTraceInCatch() + { + var engine = new Engine(); - engine.Execute(@" + engine.Execute(@" var a = function(v) { try { var a = ''.xyz(); @@ -177,52 +177,52 @@ public void GeneratedErrorHasStackTraceInCatch() } ", "custom.js"); - var e = engine.Evaluate(@"b(7)", "main.js").AsString(); + var e = engine.Evaluate(@"b(7)", "main.js").AsString(); - var stack = e; - EqualIgnoringNewLineDifferences(@" at a (v) custom.js:4:13 + var stack = e; + EqualIgnoringNewLineDifferences(@" at a (v) custom.js:4:13 at b (v) custom.js:11:10 at main.js:1:1", stack); - } + } - [Fact] - public void ErrorObjectHasOwnPropertyStack() - { - var res = new Engine().Evaluate(@"Error().hasOwnProperty('stack')").AsBoolean(); - Assert.True(res); - } + [Fact] + public void ErrorObjectHasOwnPropertyStack() + { + var res = new Engine().Evaluate(@"Error().hasOwnProperty('stack')").AsBoolean(); + Assert.True(res); + } - private class Folder - { - public Folder Parent { get; set; } - public string Name { get; set; } - } + private class Folder + { + public Folder Parent { get; set; } + public string Name { get; set; } + } - [Fact] - public void CallStackBuildingShouldSkipResolvingFromEngine() - { - var engine = new Engine(o => o.LimitRecursion(200)); - var recordedFolderTraversalOrder = new List(); - engine.SetValue("log", new Action(o => recordedFolderTraversalOrder.Add(o.ToString()))); + [Fact] + public void CallStackBuildingShouldSkipResolvingFromEngine() + { + var engine = new Engine(o => o.LimitRecursion(200)); + var recordedFolderTraversalOrder = new List(); + engine.SetValue("log", new Action(o => recordedFolderTraversalOrder.Add(o.ToString()))); - var folder = new Folder + var folder = new Folder + { + Name = "SubFolder2", + Parent = new Folder { - Name = "SubFolder2", + Name = "SubFolder1", Parent = new Folder { - Name = "SubFolder1", - Parent = new Folder - { - Name = "Root", - Parent = null, - } + Name = "Root", + Parent = null, } - }; + } + }; - engine.SetValue("folder", folder); + engine.SetValue("folder", folder); - var javaScriptException = Assert.Throws(() => - engine.Execute(@" + var javaScriptException = Assert.Throws(() => + engine.Execute(@" var Test = { recursive: function(folderInstance) { // Enabling the guard here corrects the problem, but hides the hard fault @@ -234,27 +234,27 @@ public void CallStackBuildingShouldSkipResolvingFromEngine() } Test.recursive(folder);" - )); + )); - Assert.Equal("Cannot read property 'Name' of null", javaScriptException.Message); - EqualIgnoringNewLineDifferences(@" at recursive (folderInstance) :6:44 + Assert.Equal("Cannot read property 'Name' of null", javaScriptException.Message); + EqualIgnoringNewLineDifferences(@" at recursive (folderInstance) :6:44 at recursive (folderInstance) :8:32 at recursive (folderInstance) :8:32 at recursive (folderInstance) :8:32 at :12:17", javaScriptException.JavaScriptStackTrace); - var expected = new List - { - "SubFolder2", "SubFolder1", "Root" - }; - Assert.Equal(expected, recordedFolderTraversalOrder); - } - - [Fact] - public void StackTraceCollectedOnThreeLevels() + var expected = new List { - var engine = new Engine(); - const string script = @"var a = function(v) { + "SubFolder2", "SubFolder1", "Root" + }; + Assert.Equal(expected, recordedFolderTraversalOrder); + } + + [Fact] + public void StackTraceCollectedOnThreeLevels() + { + var engine = new Engine(); + const string script = @"var a = function(v) { return v.xxx.yyy; } @@ -264,23 +264,23 @@ public void StackTraceCollectedOnThreeLevels() var x = b(7);"; - var ex = Assert.Throws(() => engine.Execute(script)); + var ex = Assert.Throws(() => engine.Execute(script)); - const string expected = @"Error: Cannot read property 'yyy' of undefined + const string expected = @"Error: Cannot read property 'yyy' of undefined at a (v) :2:18 at b (v) :6:12 at :9:9"; - EqualIgnoringNewLineDifferences(expected, ex.GetJavaScriptErrorString()); - Assert.Equal(2, ex.Location.Start.Line); - Assert.Equal(17, ex.Location.Start.Column); - } + EqualIgnoringNewLineDifferences(expected, ex.GetJavaScriptErrorString()); + Assert.Equal(2, ex.Location.Start.Line); + Assert.Equal(17, ex.Location.Start.Column); + } - [Fact] - public void StackTraceCollectedForImmediatelyInvokedFunctionExpression() - { - var engine = new Engine(); - const string script = @"function getItem(items, itemIndex) { + [Fact] + public void StackTraceCollectedForImmediatelyInvokedFunctionExpression() + { + var engine = new Engine(); + const string script = @"function getItem(items, itemIndex) { var item = items[itemIndex]; return item; @@ -294,32 +294,32 @@ public void StackTraceCollectedForImmediatelyInvokedFunctionExpression() return item; })(getItem);"; - var parsingOptions = new ScriptParsingOptions - { - CompileRegex = false, - Tolerant = true - }; - var ex = Assert.Throws(() => engine.Execute(script, "get-item.js", parsingOptions)); + var parsingOptions = new ScriptParsingOptions + { + CompileRegex = false, + Tolerant = true + }; + var ex = Assert.Throws(() => engine.Execute(script, "get-item.js", parsingOptions)); - const string expected = @"Error: Cannot read property '5' of null + const string expected = @"Error: Cannot read property '5' of null at getItem (items, itemIndex) get-item.js:2:22 at (anonymous) (getItem) get-item.js:9:16 at get-item.js:13:2"; - EqualIgnoringNewLineDifferences(expected, ex.GetJavaScriptErrorString()); + EqualIgnoringNewLineDifferences(expected, ex.GetJavaScriptErrorString()); - Assert.Equal(2, ex.Location.Start.Line); - Assert.Equal(21, ex.Location.Start.Column); - } + Assert.Equal(2, ex.Location.Start.Line); + Assert.Equal(21, ex.Location.Start.Column); + } - // Verify #1202 - [Fact] - public void StackIsUnwoundWhenExceptionHandledByInteropCode() - { - var engine = new Engine() - .SetValue("handle", new Action(Handler)); + // Verify #1202 + [Fact] + public void StackIsUnwoundWhenExceptionHandledByInteropCode() + { + var engine = new Engine() + .SetValue("handle", new Action(Handler)); - const string Script = @" + const string Script = @" function throwIt(message) { throw new Error(message); } @@ -334,135 +334,135 @@ function throwIt(message) { x.stack; // return stack trace string } "; - var stack = engine.Evaluate(Script).AsString(); - EqualIgnoringNewLineDifferences(@" at throwIt (message) :3:11 + var stack = engine.Evaluate(Script).AsString(); + EqualIgnoringNewLineDifferences(@" at throwIt (message) :3:11 at :11:5", stack); - static void Handler(Action callback) + static void Handler(Action callback) + { + try { - try - { - callback(); - } - catch (JavaScriptException) - { - // handle JS error - } + callback(); + } + catch (JavaScriptException) + { + // handle JS error } } + } - [Fact] - public void StackTraceIsForOriginalException() - { - var engine = new Engine(); - engine.SetValue("HelloWorld", new HelloWorld()); - const string script = @"HelloWorld.ThrowException();"; + [Fact] + public void StackTraceIsForOriginalException() + { + var engine = new Engine(); + engine.SetValue("HelloWorld", new HelloWorld()); + const string script = @"HelloWorld.ThrowException();"; - var ex = Assert.Throws(() => engine.Execute(script)); + var ex = Assert.Throws(() => engine.Execute(script)); - const string expected = "HelloWorld"; + const string expected = "HelloWorld"; - ContainsIgnoringNewLineDifferences(expected, ex.ToString()); - } + ContainsIgnoringNewLineDifferences(expected, ex.ToString()); + } + + [Theory] + [InlineData("Error")] + [InlineData("EvalError")] + [InlineData("RangeError")] + [InlineData("SyntaxError")] + [InlineData("TypeError")] + [InlineData("ReferenceError")] + public void ErrorsHaveCorrectConstructor(string type) + { + var engine = new Engine(); + engine.Execute($"const o = new {type}();"); + Assert.True(engine.Evaluate($"o.constructor === {type}").AsBoolean()); + Assert.Equal(type, engine.Evaluate("o.constructor.name").AsString()); + } - [Theory] - [InlineData("Error")] - [InlineData("EvalError")] - [InlineData("RangeError")] - [InlineData("SyntaxError")] - [InlineData("TypeError")] - [InlineData("ReferenceError")] - public void ErrorsHaveCorrectConstructor(string type) + [Fact] + public void CallStackWorksWithRecursiveCalls() + { + static ScriptParsingOptions CreateParsingOptions() { - var engine = new Engine(); - engine.Execute($"const o = new {type}();"); - Assert.True(engine.Evaluate($"o.constructor === {type}").AsBoolean()); - Assert.Equal(type, engine.Evaluate("o.constructor.name").AsString()); + return new ScriptParsingOptions + { + CompileRegex = false, + Tolerant = true + }; } - [Fact] - public void CallStackWorksWithRecursiveCalls() + var e = Assert.Throws(() => { - static ScriptParsingOptions CreateParsingOptions() - { - return new ScriptParsingOptions - { - CompileRegex = false, - Tolerant = true - }; - } + var engine = new Engine(); - var e = Assert.Throws(() => + engine.SetValue("executeFile", (Action) (path => { - var engine = new Engine(); - - engine.SetValue("executeFile", (Action) (path => + var content = path switch { - var content = path switch - { - "first-file.js" => @"num = num * 3; + "first-file.js" => @"num = num * 3; executeFile(""second-file.js"");", - "second-file.js" => @"// Intentionally making a mistake in the variable name + "second-file.js" => @"// Intentionally making a mistake in the variable name nuм -= 3;", - _ => throw new FileNotFoundException($"File '{path}' not exist.", path) - }; - engine.Execute(content, path, CreateParsingOptions()); - })); - engine.Execute( - @"var num = 5; + _ => throw new FileNotFoundException($"File '{path}' not exist.", path) + }; + engine.Execute(content, path, CreateParsingOptions()); + })); + engine.Execute( + @"var num = 5; executeFile(""first-file.js"");", - "main-file.js", - CreateParsingOptions() - ); - }); + "main-file.js", + CreateParsingOptions() + ); + }); - Assert.Equal("nuм is not defined", e.Message); + Assert.Equal("nuм is not defined", e.Message); - const string Expected = @" at delegate second-file.js:2:1 + const string Expected = @" at delegate second-file.js:2:1 at delegate first-file.js:2:1 at main-file.js:2:1"; - EqualIgnoringNewLineDifferences(Expected, e.JavaScriptStackTrace); - } + EqualIgnoringNewLineDifferences(Expected, e.JavaScriptStackTrace); + } - [Fact] - public void ShouldReportCorrectColumn() + [Fact] + public void ShouldReportCorrectColumn() + { + var e = Assert.Throws(() => { - var e = Assert.Throws(() => - { - var engine = new Engine(); - engine.Execute(@"var $variable1 = 611; + var engine = new Engine(); + engine.Execute(@"var $variable1 = 611; var _variable2 = 711; var variable3 = 678; $variable1 + -variable2 - variable3;"); - }); + }); - Assert.Equal(5, e.Location.Start.Line); - Assert.Equal(14, e.Location.Start.Column); - Assert.Equal(" at :5:15", e.JavaScriptStackTrace); - } + Assert.Equal(5, e.Location.Start.Line); + Assert.Equal(14, e.Location.Start.Column); + Assert.Equal(" at :5:15", e.JavaScriptStackTrace); + } - [Fact] - public void InvokingDelegateShouldContainJavascriptExceptionAsInnerException() - { - Delegate func = null; - void SetFuncValue(Delegate scriptFunc) => func = scriptFunc; + [Fact] + public void InvokingDelegateShouldContainJavascriptExceptionAsInnerException() + { + Delegate func = null; + void SetFuncValue(Delegate scriptFunc) => func = scriptFunc; - var engine = new Engine(); - engine.SetValue("SetFuncValue", SetFuncValue); - engine.Execute("SetFuncValue(() => { foo.bar });"); + var engine = new Engine(); + engine.SetValue("SetFuncValue", SetFuncValue); + engine.Execute("SetFuncValue(() => { foo.bar });"); - var ex = Assert.Throws(() => func?.DynamicInvoke(JsValue.Undefined, Array.Empty())); + var ex = Assert.Throws(() => func?.DynamicInvoke(JsValue.Undefined, Array.Empty())); - var exception = Assert.IsType(ex.InnerException); - Assert.Equal("foo is not defined", exception.Message); - } + var exception = Assert.IsType(ex.InnerException); + Assert.Equal("foo is not defined", exception.Message); + } - [Fact] - public void JavaScriptExceptionLocationOnModuleShouldBeRight() - { - var engine = new Engine(); - engine.Modules.Add("my_module", @" + [Fact] + public void JavaScriptExceptionLocationOnModuleShouldBeRight() + { + var engine = new Engine(); + engine.Modules.Add("my_module", @" function throw_error(){ throw Error(""custom error"") } @@ -470,23 +470,22 @@ function throw_error(){ throw_error(); "); - var ex= Assert.Throws(() => engine.Modules.Import("my_module")); - Assert.Equal(ex.Location.Start.Line, 3); - Assert.Equal(ex.Location.Start.Column, 10); - } + var ex= Assert.Throws(() => engine.Modules.Import("my_module")); + Assert.Equal(ex.Location.Start.Line, 3); + Assert.Equal(ex.Location.Start.Column, 10); + } - private static void EqualIgnoringNewLineDifferences(string expected, string actual) - { - expected = expected.Replace("\r\n", "\n"); - actual = actual.Replace("\r\n", "\n"); - Assert.Equal(expected, actual); - } + private static void EqualIgnoringNewLineDifferences(string expected, string actual) + { + expected = expected.Replace("\r\n", "\n"); + actual = actual.Replace("\r\n", "\n"); + Assert.Equal(expected, actual); + } - private static void ContainsIgnoringNewLineDifferences(string expectedSubstring, string actualString) - { - expectedSubstring = expectedSubstring.Replace("\r\n", "\n"); - actualString = actualString.Replace("\r\n", "\n"); - Assert.Contains(expectedSubstring, actualString); - } + private static void ContainsIgnoringNewLineDifferences(string expectedSubstring, string actualString) + { + expectedSubstring = expectedSubstring.Replace("\r\n", "\n"); + actualString = actualString.Replace("\r\n", "\n"); + Assert.Contains(expectedSubstring, actualString); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/ExecutionConstraintTests.cs b/Jint.Tests/Runtime/ExecutionConstraintTests.cs index ca2315ba9d..31b45de3f2 100644 --- a/Jint.Tests/Runtime/ExecutionConstraintTests.cs +++ b/Jint.Tests/Runtime/ExecutionConstraintTests.cs @@ -1,53 +1,53 @@ using Jint.Native.Function; using Jint.Runtime; -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class ExecutionConstraintTests { - public class ExecutionConstraintTests + [Fact] + public void ShouldThrowStatementCountOverflow() { - [Fact] - public void ShouldThrowStatementCountOverflow() - { - Assert.Throws( - () => new Engine(cfg => cfg.MaxStatements(100)).Evaluate("while(true);") - ); - } + Assert.Throws( + () => new Engine(cfg => cfg.MaxStatements(100)).Evaluate("while(true);") + ); + } - [Fact] - public void ShouldThrowMemoryLimitExceeded() - { - Assert.Throws( - () => new Engine(cfg => cfg.LimitMemory(2048)).Evaluate("a=[]; while(true){ a.push(0); }") - ); - } + [Fact] + public void ShouldThrowMemoryLimitExceeded() + { + Assert.Throws( + () => new Engine(cfg => cfg.LimitMemory(2048)).Evaluate("a=[]; while(true){ a.push(0); }") + ); + } - [Fact] - public void ShouldThrowTimeout() - { - Assert.Throws( - () => new Engine(cfg => cfg.TimeoutInterval(new TimeSpan(0, 0, 0, 0, 500))).Evaluate("while(true);") - ); - } + [Fact] + public void ShouldThrowTimeout() + { + Assert.Throws( + () => new Engine(cfg => cfg.TimeoutInterval(new TimeSpan(0, 0, 0, 0, 500))).Evaluate("while(true);") + ); + } - [Fact] - public void ShouldThrowExecutionCanceled() - { - Assert.Throws( - () => + [Fact] + public void ShouldThrowExecutionCanceled() + { + Assert.Throws( + () => + { + using (var tcs = new CancellationTokenSource()) + using (var waitHandle = new ManualResetEvent(false)) { - using (var tcs = new CancellationTokenSource()) - using (var waitHandle = new ManualResetEvent(false)) - { - var engine = new Engine(cfg => cfg.CancellationToken(tcs.Token)); + var engine = new Engine(cfg => cfg.CancellationToken(tcs.Token)); - ThreadPool.QueueUserWorkItem(state => - { - waitHandle.WaitOne(); - tcs.Cancel(); - }); + ThreadPool.QueueUserWorkItem(state => + { + waitHandle.WaitOne(); + tcs.Cancel(); + }); - engine.SetValue("waitHandle", waitHandle); - engine.Evaluate(@" + engine.SetValue("waitHandle", waitHandle); + engine.Evaluate(@" function sleep(millisecondsTimeout) { var totalMilliseconds = new Date().getTime() + millisecondsTimeout; @@ -62,15 +62,15 @@ function sleep(millisecondsTimeout) { sleep(1000); sleep(1000); "); - } } - ); - } + } + ); + } - [Fact] - public void CanDiscardRecursion() - { - var script = @"var factorial = function(n) { + [Fact] + public void CanDiscardRecursion() + { + var script = @"var factorial = function(n) { if (n>1) { return n * factorial(n - 1); } @@ -79,15 +79,15 @@ public void CanDiscardRecursion() var result = factorial(500); "; - Assert.Throws( - () => new Engine(cfg => cfg.LimitRecursion()).Execute(script) - ); - } + Assert.Throws( + () => new Engine(cfg => cfg.LimitRecursion()).Execute(script) + ); + } - [Fact] - public void ShouldDiscardHiddenRecursion() - { - var script = @"var renamedFunc; + [Fact] + public void ShouldDiscardHiddenRecursion() + { + var script = @"var renamedFunc; var exec = function(callback) { renamedFunc = callback; callback(); @@ -98,15 +98,15 @@ public void ShouldDiscardHiddenRecursion() }); "; - Assert.Throws( - () => new Engine(cfg => cfg.LimitRecursion()).Execute(script) - ); - } + Assert.Throws( + () => new Engine(cfg => cfg.LimitRecursion()).Execute(script) + ); + } - [Fact] - public void ShouldRecognizeAndDiscardChainedRecursion() - { - var script = @" var funcRoot, funcA, funcB, funcC, funcD; + [Fact] + public void ShouldRecognizeAndDiscardChainedRecursion() + { + var script = @" var funcRoot, funcA, funcB, funcC, funcD; var funcRoot = function() { funcA(); @@ -131,15 +131,15 @@ public void ShouldRecognizeAndDiscardChainedRecursion() funcRoot(); "; - Assert.Throws( - () => new Engine(cfg => cfg.LimitRecursion()).Execute(script) - ); - } + Assert.Throws( + () => new Engine(cfg => cfg.LimitRecursion()).Execute(script) + ); + } - [Fact] - public void ShouldProvideCallChainWhenDiscardRecursion() - { - var script = @" var funcRoot, funcA, funcB, funcC, funcD; + [Fact] + public void ShouldProvideCallChainWhenDiscardRecursion() + { + var script = @" var funcRoot, funcA, funcB, funcC, funcD; var funcRoot = function() { funcA(); @@ -164,26 +164,26 @@ public void ShouldProvideCallChainWhenDiscardRecursion() funcRoot(); "; - RecursionDepthOverflowException exception = null; + 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); + try + { + new Engine(cfg => cfg.LimitRecursion()).Execute(script); } - - [Fact] - public void ShouldAllowShallowRecursion() + catch (RecursionDepthOverflowException ex) { - var script = @"var factorial = function(n) { + 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); } @@ -192,13 +192,13 @@ public void ShouldAllowShallowRecursion() var result = factorial(8); "; - new Engine(cfg => cfg.LimitRecursion(20)).Execute(script); - } + new Engine(cfg => cfg.LimitRecursion(20)).Execute(script); + } - [Fact] - public void ShouldDiscardDeepRecursion() - { - var script = @"var factorial = function(n) { + [Fact] + public void ShouldDiscardDeepRecursion() + { + var script = @"var factorial = function(n) { if (n>1) { return n * factorial(n - 1); } @@ -207,15 +207,15 @@ public void ShouldDiscardDeepRecursion() var result = factorial(38); "; - Assert.Throws( - () => new Engine(cfg => cfg.LimitRecursion(20)).Execute(script) - ); - } + Assert.Throws( + () => new Engine(cfg => cfg.LimitRecursion(20)).Execute(script) + ); + } - [Fact] - public void ShouldAllowRecursionLimitWithoutReferencedName() - { - const string input = @"(function () { + [Fact] + public void ShouldAllowRecursionLimitWithoutReferencedName() + { + const string input = @"(function () { var factorial = function(n) { if (n>1) { return n * factorial(n - 1); @@ -226,21 +226,21 @@ public void ShouldAllowRecursionLimitWithoutReferencedName() })(); "; - var engine = new Engine(o => o.LimitRecursion(20)); - Assert.Throws(() => engine.Execute(input)); - } + var engine = new Engine(o => o.LimitRecursion(20)); + Assert.Throws(() => engine.Execute(input)); + } - [Fact] - public void ShouldLimitRecursionWithAllFunctionInstances() + [Fact] + public void ShouldLimitRecursionWithAllFunctionInstances() + { + var engine = new Engine(cfg => { - var engine = new Engine(cfg => - { - // Limit recursion to 5 invocations - cfg.LimitRecursion(5); - cfg.Strict(); - }); + // Limit recursion to 5 invocations + cfg.LimitRecursion(5); + cfg.Strict(); + }); - var ex = Assert.Throws(() => engine.Evaluate(@" + var ex = Assert.Throws(() => engine.Evaluate(@" var myarr = new Array(5000); for (var i = 0; i < myarr.length; i++) { myarr[i] = function(i) { @@ -250,131 +250,130 @@ public void ShouldLimitRecursionWithAllFunctionInstances() 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)); + [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(() => engine.Evaluate(code)); - } + Assert.Throws(() => engine.Evaluate(code)); + } - [Fact] - public void ShouldLimitArraySizeForConcat() - { - var engine = new Engine(o => o.MaxStatements(1_000).MaxArraySize(1_000_000)); - Assert.Throws(() => engine.Evaluate("for (let a = [1, 2, 3];; a = a.concat(a)) ;")); - } + [Fact] + public void ShouldLimitArraySizeForConcat() + { + var engine = new Engine(o => o.MaxStatements(1_000).MaxArraySize(1_000_000)); + Assert.Throws(() => 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(() => engine.Evaluate("var arr = Array(1000000000).fill(new Array(1000000000));")); - } + [Fact] + public void ShouldLimitArraySizeForFill() + { + var engine = new Engine(o => o.MaxStatements(1_000).MaxArraySize(1_000_000)); + Assert.Throws(() => 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(() => engine.Evaluate("new Array(2147483647).join('*')")); - } + [Fact] + public void ShouldLimitArraySizeForJoin() + { + var engine = new Engine(o => o.MaxStatements(1_000).MaxArraySize(1_000_000)); + Assert.Throws(() => engine.Evaluate("new Array(2147483647).join('*')")); + } - [Fact] - public void ShouldConsiderConstraintsWhenCallingInvoke() + [Fact] + public void ShouldConsiderConstraintsWhenCallingInvoke() + { + var engine = new Engine(options => { - var engine = new Engine(options => - { - options.TimeoutInterval(TimeSpan.FromMilliseconds(100)); - }); - var myApi = new MyApi(); + options.TimeoutInterval(TimeSpan.FromMilliseconds(100)); + }); + var myApi = new MyApi(); - engine.SetValue("myApi", myApi); - engine.Execute("myApi.addEventListener('DataReceived', (data) => { myApi.log(data) })"); + engine.SetValue("myApi", myApi); + engine.Execute("myApi.addEventListener('DataReceived', (data) => { myApi.log(data) })"); - var dataReceivedCallbacks = myApi.Callbacks.Where(kvp => kvp.Key == "DataReceived"); - foreach (var callback in dataReceivedCallbacks) - { - engine.Invoke(callback.Value, "Data Received #1"); - Thread.Sleep(101); - engine.Invoke(callback.Value, "Data Received #2"); - } + var dataReceivedCallbacks = myApi.Callbacks.Where(kvp => kvp.Key == "DataReceived"); + foreach (var callback in dataReceivedCallbacks) + { + engine.Invoke(callback.Value, "Data Received #1"); + Thread.Sleep(101); + engine.Invoke(callback.Value, "Data Received #2"); } + } - [Fact] - public void ResetConstraints() - { - void ExecuteAction(Engine engine) => engine.Execute("recursion()"); - void InvokeAction(Engine engine) => engine.Invoke("recursion"); + [Fact] + public void ResetConstraints() + { + void ExecuteAction(Engine engine) => engine.Execute("recursion()"); + void InvokeAction(Engine engine) => engine.Invoke("recursion"); - List expected = [6, 6, 6, 6, 6]; - Assert.Equal(expected, RunLoop(CreateEngine(), ExecuteAction)); - Assert.Equal(expected, RunLoop(CreateEngine(), InvokeAction)); + List expected = [6, 6, 6, 6, 6]; + Assert.Equal(expected, RunLoop(CreateEngine(), ExecuteAction)); + Assert.Equal(expected, RunLoop(CreateEngine(), InvokeAction)); - var e1 = CreateEngine(); - Assert.Equal(expected, RunLoop(e1, ExecuteAction)); - Assert.Equal(expected, RunLoop(e1, InvokeAction)); + var e1 = CreateEngine(); + Assert.Equal(expected, RunLoop(e1, ExecuteAction)); + Assert.Equal(expected, RunLoop(e1, InvokeAction)); - var e2 = CreateEngine(); - Assert.Equal(expected, RunLoop(e2, InvokeAction)); - Assert.Equal(expected, RunLoop(e2, ExecuteAction)); + var e2 = CreateEngine(); + Assert.Equal(expected, RunLoop(e2, InvokeAction)); + Assert.Equal(expected, RunLoop(e2, ExecuteAction)); - var e3 = CreateEngine(); - Assert.Equal(expected, RunLoop(e3, InvokeAction)); - Assert.Equal(expected, RunLoop(e3, ExecuteAction)); - Assert.Equal(expected, RunLoop(e3, InvokeAction)); + var e3 = CreateEngine(); + Assert.Equal(expected, RunLoop(e3, InvokeAction)); + Assert.Equal(expected, RunLoop(e3, ExecuteAction)); + Assert.Equal(expected, RunLoop(e3, InvokeAction)); - var e4 = CreateEngine(); - Assert.Equal(expected, RunLoop(e4, InvokeAction)); - Assert.Equal(expected, RunLoop(e4, InvokeAction)); - } + var e4 = CreateEngine(); + Assert.Equal(expected, RunLoop(e4, InvokeAction)); + Assert.Equal(expected, RunLoop(e4, InvokeAction)); + } - private static Engine CreateEngine() - { - Engine engine = new(options => options.LimitRecursion(5)); - return engine.Execute(""" - var num = 0; - function recursion() { - num++; - recursion(num); - } - """); - } + private static Engine CreateEngine() + { + Engine engine = new(options => options.LimitRecursion(5)); + return engine.Execute(""" + var num = 0; + function recursion() { + num++; + recursion(num); + } + """); + } - private static List RunLoop(Engine engine, Action engineAction) + private static List RunLoop(Engine engine, Action engineAction) + { + List results = new(); + for (var i = 0; i < 5; i++) { - List results = new(); - for (var i = 0; i < 5; i++) + try { - try - { - engine.SetValue("num", 0); - engineAction.Invoke(engine); - } - catch (RecursionDepthOverflowException) - { - results.Add((int) engine.GetValue("num").AsNumber()); - } + engine.SetValue("num", 0); + engineAction.Invoke(engine); + } + catch (RecursionDepthOverflowException) + { + results.Add((int) engine.GetValue("num").AsNumber()); } - - return results; } - private class MyApi - { - public readonly Dictionary Callbacks = new Dictionary(); + return results; + } - public void AddEventListener(string eventName, ScriptFunction callback) - { - Callbacks.Add(eventName, callback); - } + private class MyApi + { + public readonly Dictionary Callbacks = new Dictionary(); - public void Log(string logMessage) - { - Console.WriteLine(logMessage); - } + public void AddEventListener(string eventName, ScriptFunction callback) + { + Callbacks.Add(eventName, callback); + } + + public void Log(string logMessage) + { + Console.WriteLine(logMessage); } } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/ExtensionMethods/CustomStringExtensions.cs b/Jint.Tests/Runtime/ExtensionMethods/CustomStringExtensions.cs index 3070874118..2ea629842a 100644 --- a/Jint.Tests/Runtime/ExtensionMethods/CustomStringExtensions.cs +++ b/Jint.Tests/Runtime/ExtensionMethods/CustomStringExtensions.cs @@ -1,35 +1,34 @@ using System.Dynamic; using Newtonsoft.Json; -namespace Jint.Tests.Runtime.ExtensionMethods +namespace Jint.Tests.Runtime.ExtensionMethods; + +public static class CustomStringExtensions { - public static class CustomStringExtensions + public static string Backwards(this string value) { - public static string Backwards(this string value) - { - return new string(value.Reverse().ToArray()); - } + return new string(value.Reverse().ToArray()); + } - public static T DeserializeObject(this string json) - { - return JsonConvert.DeserializeObject(json); - } + public static T DeserializeObject(this string json) + { + return JsonConvert.DeserializeObject(json); + } - public static ExpandoObject DeserializeObject(this string json) - { - return DeserializeObject(json); - } + public static ExpandoObject DeserializeObject(this string json) + { + return DeserializeObject(json); + } - public static string[] Split(this string value, string split, StringSplitOptions options) - { - return Array.Empty(); - } + public static string[] Split(this string value, string split, StringSplitOptions options) + { + return Array.Empty(); + } - public static string[] Split(this string value, int position) - { - var first = value.Substring(0, position); - var second = value.Substring(position); - return new string[] { first, second }; - } + public static string[] Split(this string value, int position) + { + var first = value.Substring(0, position); + var second = value.Substring(position); + return new string[] { first, second }; } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/ExtensionMethods/DoubleExtensions.cs b/Jint.Tests/Runtime/ExtensionMethods/DoubleExtensions.cs index f83f65e53c..f6a6503515 100644 --- a/Jint.Tests/Runtime/ExtensionMethods/DoubleExtensions.cs +++ b/Jint.Tests/Runtime/ExtensionMethods/DoubleExtensions.cs @@ -1,10 +1,9 @@ -namespace Jint.Tests.Runtime.ExtensionMethods +namespace Jint.Tests.Runtime.ExtensionMethods; + +public static class DoubleExtensions { - public static class DoubleExtensions + public static double Add(this double integer, int add) { - public static double Add(this double integer, int add) - { - return integer + add; - } + return integer + add; } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/ExtensionMethods/ExtensionMethodsTest.cs b/Jint.Tests/Runtime/ExtensionMethods/ExtensionMethodsTest.cs index c47c9581de..9bb5eafb71 100644 --- a/Jint.Tests/Runtime/ExtensionMethods/ExtensionMethodsTest.cs +++ b/Jint.Tests/Runtime/ExtensionMethods/ExtensionMethodsTest.cs @@ -1,134 +1,134 @@ using Jint.Native; using Jint.Tests.Runtime.Domain; -namespace Jint.Tests.Runtime.ExtensionMethods +namespace Jint.Tests.Runtime.ExtensionMethods; + +public class ExtensionMethodsTest { - public class ExtensionMethodsTest + [Fact] + public void ShouldInvokeObjectExtensionMethod() { - [Fact] - public void ShouldInvokeObjectExtensionMethod() - { - var person = new Person(); - person.Name = "Mickey Mouse"; - person.Age = 35; + var person = new Person(); + person.Name = "Mickey Mouse"; + person.Age = 35; - var options = new Options(); - options.AddExtensionMethods(typeof(PersonExtensions)); + var options = new Options(); + options.AddExtensionMethods(typeof(PersonExtensions)); - var engine = new Engine(options); - engine.SetValue("person", person); - var age = engine.Evaluate("person.MultiplyAge(2)").AsInteger(); + var engine = new Engine(options); + engine.SetValue("person", person); + var age = engine.Evaluate("person.MultiplyAge(2)").AsInteger(); - Assert.Equal(70, age); - } + Assert.Equal(70, age); + } - [Fact] - public void ShouldInvokeStringExtensionMethod() - { - var options = new Options(); - options.AddExtensionMethods(typeof(CustomStringExtensions)); + [Fact] + public void ShouldInvokeStringExtensionMethod() + { + var options = new Options(); + options.AddExtensionMethods(typeof(CustomStringExtensions)); - var engine = new Engine(options); - var result = engine.Evaluate("\"Hello World!\".Backwards()").AsString(); + var engine = new Engine(options); + var result = engine.Evaluate("\"Hello World!\".Backwards()").AsString(); - Assert.Equal("!dlroW olleH", result); - } + Assert.Equal("!dlroW olleH", result); + } - [Fact] - public void ShouldInvokeNumberExtensionMethod() - { - var options = new Options(); - options.AddExtensionMethods(typeof(DoubleExtensions)); + [Fact] + public void ShouldInvokeNumberExtensionMethod() + { + var options = new Options(); + options.AddExtensionMethods(typeof(DoubleExtensions)); - var engine = new Engine(options); - var result = engine.Evaluate("let numb = 27; numb.Add(13)").AsInteger(); + var engine = new Engine(options); + var result = engine.Evaluate("let numb = 27; numb.Add(13)").AsInteger(); - Assert.Equal(40, result); - } + Assert.Equal(40, result); + } - [Fact] - public void ShouldPrioritizingNonGenericMethod() - { - var options = new Options(); - options.AddExtensionMethods(typeof(CustomStringExtensions)); + [Fact] + public void ShouldPrioritizingNonGenericMethod() + { + var options = new Options(); + options.AddExtensionMethods(typeof(CustomStringExtensions)); - var engine = new Engine(options); - var result = engine.Evaluate("\"{'name':'Mickey'}\".DeserializeObject()").ToObject() as dynamic; + var engine = new Engine(options); + var result = engine.Evaluate("\"{'name':'Mickey'}\".DeserializeObject()").ToObject() as dynamic; - Assert.Equal("Mickey", result.name); - } + Assert.Equal("Mickey", result.name); + } - [Fact] - public void PrototypeFunctionsShouldNotBeOverridden() + [Fact] + public void PrototypeFunctionsShouldNotBeOverridden() + { + var engine = new Engine(opts => { - var engine = new Engine(opts => - { - opts.AddExtensionMethods(typeof(CustomStringExtensions)); - }); - - //uses split function from StringPrototype - var arr = engine.Evaluate("'yes,no'.split(',')").AsArray(); - Assert.Equal("yes", arr[0]); - Assert.Equal("no", arr[1]); - - //uses split function from CustomStringExtensions - var arr2 = engine.Evaluate("'yes,no'.split(2)").AsArray(); - Assert.Equal("ye", arr2[0]); - Assert.Equal("s,no", arr2[1]); - } + opts.AddExtensionMethods(typeof(CustomStringExtensions)); + }); + + //uses split function from StringPrototype + var arr = engine.Evaluate("'yes,no'.split(',')").AsArray(); + Assert.Equal("yes", arr[0]); + Assert.Equal("no", arr[1]); + + //uses split function from CustomStringExtensions + var arr2 = engine.Evaluate("'yes,no'.split(2)").AsArray(); + Assert.Equal("ye", arr2[0]); + Assert.Equal("s,no", arr2[1]); + } - [Fact] - public void OverridePrototypeFunctions() + [Fact] + public void OverridePrototypeFunctions() + { + var engine = new Engine(opts => { - var engine = new Engine(opts => - { - opts.AddExtensionMethods(typeof(OverrideStringPrototypeExtensions)); - }); - - //uses the overridden split function from OverrideStringPrototypeExtensions - var arr = engine.Evaluate("'yes,no'.split(',')").AsArray(); - Assert.Equal("YES", arr[0]); - Assert.Equal("NO", arr[1]); - } + opts.AddExtensionMethods(typeof(OverrideStringPrototypeExtensions)); + }); - [Fact] - public void HasOwnPropertyShouldWorkCorrectlyInPresenceOfExtensionMethods() - { - var person = new Person(); + //uses the overridden split function from OverrideStringPrototypeExtensions + var arr = engine.Evaluate("'yes,no'.split(',')").AsArray(); + Assert.Equal("YES", arr[0]); + Assert.Equal("NO", arr[1]); + } - var options = new Options(); - options.AddExtensionMethods(typeof(PersonExtensions)); + [Fact] + public void HasOwnPropertyShouldWorkCorrectlyInPresenceOfExtensionMethods() + { + var person = new Person(); - var engine = new Engine(options); - engine.SetValue("person", person); + var options = new Options(); + options.AddExtensionMethods(typeof(PersonExtensions)); - var isBogusInPerson = engine.Evaluate("'bogus' in person").AsBoolean(); - Assert.False(isBogusInPerson); + var engine = new Engine(options); + engine.SetValue("person", person); - var propertyValue = engine.Evaluate("person.bogus"); - Assert.Equal(JsValue.Undefined, propertyValue); - } + var isBogusInPerson = engine.Evaluate("'bogus' in person").AsBoolean(); + Assert.False(isBogusInPerson); - private Engine GetLinqEngine() - { - return new Engine(opts => - { - opts.AddExtensionMethods(typeof(Enumerable)); - }); - } + var propertyValue = engine.Evaluate("person.bogus"); + Assert.Equal(JsValue.Undefined, propertyValue); + } - [Fact] - public void LinqExtensionMethodWithoutGenericParameter() + private Engine GetLinqEngine() + { + return new Engine(opts => { - var engine = GetLinqEngine(); - var intList = new List() { 0, 1, 2, 3 }; + opts.AddExtensionMethods(typeof(Enumerable)); + }); + } - engine.SetValue("intList", intList); - var intSumRes = engine.Evaluate("intList.Sum()").AsNumber(); - Assert.Equal(6, intSumRes); - } + [Fact] + public void LinqExtensionMethodWithoutGenericParameter() + { + var engine = GetLinqEngine(); + var intList = new List() { 0, 1, 2, 3 }; - // TODO this fails due to double -> long assignment on FW + engine.SetValue("intList", intList); + var intSumRes = engine.Evaluate("intList.Sum()").AsNumber(); + Assert.Equal(6, intSumRes); + } + + // TODO this fails due to double -> long assignment on FW #if !NETFRAMEWORK [Fact] public void LinqExtensionMethodWithSingleGenericParameter() @@ -142,35 +142,35 @@ public void LinqExtensionMethodWithSingleGenericParameter() } #endif - [Fact] - public void LinqExtensionMethodWithMultipleGenericParameters() - { - var engine = GetLinqEngine(); - var stringList = new List() { "working", "linq" }; - engine.SetValue("stringList", stringList); + [Fact] + public void LinqExtensionMethodWithMultipleGenericParameters() + { + var engine = GetLinqEngine(); + var stringList = new List() { "working", "linq" }; + engine.SetValue("stringList", stringList); - var stringRes = engine.Evaluate("stringList.Select((x) => x + 'a').ToArray().join()").AsString(); - Assert.Equal("workinga,linqa", stringRes); + var stringRes = engine.Evaluate("stringList.Select((x) => x + 'a').ToArray().join()").AsString(); + Assert.Equal("workinga,linqa", stringRes); - // The method ambiguity resolver is not so smart to choose the Select method with the correct number of parameters - // Thus, the following script will not work as expected. - // stringList.Select((x, i) => x + i).ToArray().join() - } + // The method ambiguity resolver is not so smart to choose the Select method with the correct number of parameters + // Thus, the following script will not work as expected. + // stringList.Select((x, i) => x + i).ToArray().join() + } - [Fact] - public void GenericTypeExtension() - { - var options = new Options(); - options.AddExtensionMethods(typeof(ObservableExtensions)); + [Fact] + public void GenericTypeExtension() + { + var options = new Options(); + options.AddExtensionMethods(typeof(ObservableExtensions)); - var engine = new Engine(options); + var engine = new Engine(options); - engine.SetValue("log", new System.Action(System.Console.WriteLine)); + engine.SetValue("log", new System.Action(System.Console.WriteLine)); - NameObservable observable = new NameObservable(); + NameObservable observable = new NameObservable(); - engine.SetValue("observable", observable); - engine.Evaluate(@" + engine.SetValue("observable", observable); + engine.Evaluate(@" log('before'); observable.Subscribe((name) =>{ log('observable: subscribe: name: ' + name); @@ -180,49 +180,49 @@ public void GenericTypeExtension() log('after'); "); - Assert.Equal("foobar", observable.Last); - } + Assert.Equal("foobar", observable.Last); + } - [Fact] - public void GenericExtensionMethodOnClosedGenericType() - { - var options = new Options(); - options.AddExtensionMethods(typeof(ObservableExtensions)); + [Fact] + public void GenericExtensionMethodOnClosedGenericType() + { + var options = new Options(); + options.AddExtensionMethods(typeof(ObservableExtensions)); - var engine = new Engine(options); + var engine = new Engine(options); - engine.SetValue("log", new System.Action(System.Console.WriteLine)); + engine.SetValue("log", new System.Action(System.Console.WriteLine)); - NameObservable observable = new NameObservable(); - engine.SetValue("observable", observable); - var result = engine.Evaluate(@" + NameObservable observable = new NameObservable(); + engine.SetValue("observable", observable); + var result = engine.Evaluate(@" log('before calling Select'); var result = observable.Select('some text'); log('result: ' + result); return result; "); - //System.Console.WriteLine("GenericExtensionMethodOnGenericType: result: " + result + " result.ToString(): " + result.ToString()); + //System.Console.WriteLine("GenericExtensionMethodOnGenericType: result: " + result + " result.ToString(): " + result.ToString()); - Assert.Equal("some text", result); - } + Assert.Equal("some text", result); + } - [Fact] - public void GenericExtensionMethodOnClosedGenericType2() + [Fact] + public void GenericExtensionMethodOnClosedGenericType2() + { + var options = new Options(); + options.AddExtensionMethods(typeof(ObservableExtensions)); + + var engine = new Engine(options); + + NameObservable observable = new NameObservable(); + observable.Where((text) => { - var options = new Options(); - options.AddExtensionMethods(typeof(ObservableExtensions)); - - var engine = new Engine(options); - - NameObservable observable = new NameObservable(); - observable.Where((text) => - { - System.Console.WriteLine("GenericExtensionMethodOnClosedGenericType2: NameObservable: Where: text: " + text); - return true; - }); - engine.SetValue("observable", observable); - var result = engine.Evaluate(@" + System.Console.WriteLine("GenericExtensionMethodOnClosedGenericType2: NameObservable: Where: text: " + text); + return true; + }); + engine.SetValue("observable", observable); + var result = engine.Evaluate(@" var result = observable.Where(function(text){ return true; }); @@ -232,27 +232,27 @@ public void GenericExtensionMethodOnClosedGenericType2() return result; "); - var nameObservableResult = result.ToObject() as NameObservable; - Assert.NotNull(nameObservableResult); - Assert.Equal("testing yo", nameObservableResult.Last); - } + var nameObservableResult = result.ToObject() as NameObservable; + Assert.NotNull(nameObservableResult); + Assert.Equal("testing yo", nameObservableResult.Last); + } - [Fact] - public void GenericExtensionMethodOnOpenGenericType() + [Fact] + public void GenericExtensionMethodOnOpenGenericType() + { + var options = new Options(); + options.AddExtensionMethods(typeof(ObservableExtensions)); + + var engine = new Engine(options); + + BaseObservable observable = new BaseObservable(); + observable.Where((text) => { - var options = new Options(); - options.AddExtensionMethods(typeof(ObservableExtensions)); - - var engine = new Engine(options); - - BaseObservable observable = new BaseObservable(); - observable.Where((text) => - { - System.Console.WriteLine("GenericExtensionMethodOnOpenGenericType: BaseObservable: Where: text: " + text); - return true; - }); - engine.SetValue("observable", observable); - var result = engine.Evaluate(@" + System.Console.WriteLine("GenericExtensionMethodOnOpenGenericType: BaseObservable: Where: text: " + text); + return true; + }); + engine.SetValue("observable", observable); + var result = engine.Evaluate(@" var result = observable.Where(function(text){ return true; }); @@ -263,26 +263,26 @@ public void GenericExtensionMethodOnOpenGenericType() return result; "); - System.Console.WriteLine("GenericExtensionMethodOnOpenGenericType: result: " + result + " result.ToString(): " + result.ToString()); - var baseObservableResult = result.ToObject() as BaseObservable; + System.Console.WriteLine("GenericExtensionMethodOnOpenGenericType: result: " + result + " result.ToString(): " + result.ToString()); + var baseObservableResult = result.ToObject() as BaseObservable; - System.Console.WriteLine("GenericExtensionMethodOnOpenGenericType: baseObservableResult: " + baseObservableResult); - Assert.NotNull(baseObservableResult); - Assert.Equal("testing yo", baseObservableResult.Last); - } + System.Console.WriteLine("GenericExtensionMethodOnOpenGenericType: baseObservableResult: " + baseObservableResult); + Assert.NotNull(baseObservableResult); + Assert.Equal("testing yo", baseObservableResult.Last); + } - [Fact] - public void GenericExtensionMethodOnGenericTypeInstantiatedInJs() - { - var options = new Options(); - options.AddExtensionMethods(typeof(ObservableExtensions)); + [Fact] + public void GenericExtensionMethodOnGenericTypeInstantiatedInJs() + { + var options = new Options(); + options.AddExtensionMethods(typeof(ObservableExtensions)); - var engine = new Engine(options); + var engine = new Engine(options); - engine.SetValue("BaseObservable", typeof(BaseObservable<>)); - engine.SetValue("ObservableFactory", typeof(ObservableFactory)); + engine.SetValue("BaseObservable", typeof(BaseObservable<>)); + engine.SetValue("ObservableFactory", typeof(ObservableFactory)); - var result = engine.Evaluate(@" + var result = engine.Evaluate(@" // you can't instantiate generic types in JS (without providing the types as arguments to the constructor) - i.e. not compatible with transpiled typescript //const observable = new BaseObservable(); @@ -298,30 +298,29 @@ public void GenericExtensionMethodOnGenericTypeInstantiatedInJs() return result; "); - var baseObservableResult = result.ToObject() as BaseObservable; + var baseObservableResult = result.ToObject() as BaseObservable; - System.Console.WriteLine("GenericExtensionMethodOnOpenGenericType: baseObservableResult: " + baseObservableResult); - Assert.NotNull(baseObservableResult); - Assert.Equal(false, baseObservableResult.Last); - } + System.Console.WriteLine("GenericExtensionMethodOnOpenGenericType: baseObservableResult: " + baseObservableResult); + Assert.NotNull(baseObservableResult); + Assert.Equal(false, baseObservableResult.Last); + } - [Fact] - public void CanProjectAndGroupWhenExpandoObjectWrappingDisabled() + [Fact] + public void CanProjectAndGroupWhenExpandoObjectWrappingDisabled() + { + var engine = new Engine(options => { - var engine = new Engine(options => - { - options.AllowClr().AddExtensionMethods(typeof(Enumerable)); - // prevent ExpandoObject wrapping - options.Interop.CreateClrObject = null; - }); - engine.Execute("var a = [ 2, 4 ];"); - - var selected = engine.Evaluate("JSON.stringify(a.Select(m => ({a:m,b:m})).ToArray());").AsString(); - Assert.Equal("""[{"a":2,"b":2},{"a":4,"b":4}]""", selected); - - var grouped1 = engine.Evaluate("a.GroupBy(m => ({a:m,b:m})).ToArray()").AsArray(); - var grouped = engine.Evaluate("JSON.stringify(a.GroupBy(m => ({a:m,b:m})).Select(x => x.Key).ToArray());").AsString(); - Assert.Equal("""[{"a":2,"b":2},{"a":4,"b":4}]""", grouped); - } + options.AllowClr().AddExtensionMethods(typeof(Enumerable)); + // prevent ExpandoObject wrapping + options.Interop.CreateClrObject = null; + }); + engine.Execute("var a = [ 2, 4 ];"); + + var selected = engine.Evaluate("JSON.stringify(a.Select(m => ({a:m,b:m})).ToArray());").AsString(); + Assert.Equal("""[{"a":2,"b":2},{"a":4,"b":4}]""", selected); + + var grouped1 = engine.Evaluate("a.GroupBy(m => ({a:m,b:m})).ToArray()").AsArray(); + var grouped = engine.Evaluate("JSON.stringify(a.GroupBy(m => ({a:m,b:m})).Select(x => x.Key).ToArray());").AsString(); + Assert.Equal("""[{"a":2,"b":2},{"a":4,"b":4}]""", grouped); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/ExtensionMethods/FlurlExtensionTest.cs b/Jint.Tests/Runtime/ExtensionMethods/FlurlExtensionTest.cs index 0eec06fc15..21bbde5d46 100644 --- a/Jint.Tests/Runtime/ExtensionMethods/FlurlExtensionTest.cs +++ b/Jint.Tests/Runtime/ExtensionMethods/FlurlExtensionTest.cs @@ -1,20 +1,20 @@ using Flurl.Http; -namespace Jint.Tests.Runtime.ExtensionMethods +namespace Jint.Tests.Runtime.ExtensionMethods; + +public class FlurlExtensionTest { - public class FlurlExtensionTest + [RunnableInDebugOnlyAttribute] + public void CanUseFlurlExtensionMethods() { - [RunnableInDebugOnlyAttribute] - public void CanUseFlurlExtensionMethods() + var engine = new Engine(options => { - var engine = new Engine(options => - { - options.AddExtensionMethods( - typeof(GeneratedExtensions), - typeof(Flurl.GeneratedExtensions)); - }); + options.AddExtensionMethods( + typeof(GeneratedExtensions), + typeof(Flurl.GeneratedExtensions)); + }); - const string script = @" + const string script = @" var result = 'https://httpbin.org/anything' .AppendPathSegment('person') .SetQueryParams({ a: 1, b: 2 }) @@ -25,9 +25,8 @@ public void CanUseFlurlExtensionMethods() }).GetAwaiter().GetResult(); "; - engine.Execute(script); + engine.Execute(script); - var result = engine.GetValue("result").ToObject(); - } + var result = engine.GetValue("result").ToObject(); } } \ No newline at end of file diff --git a/Jint.Tests/Runtime/ExtensionMethods/ObservableExtensions.cs b/Jint.Tests/Runtime/ExtensionMethods/ObservableExtensions.cs index d770ee9edb..68f5950799 100644 --- a/Jint.Tests/Runtime/ExtensionMethods/ObservableExtensions.cs +++ b/Jint.Tests/Runtime/ExtensionMethods/ObservableExtensions.cs @@ -1,141 +1,140 @@ -namespace Jint.Tests.Runtime.ExtensionMethods +namespace Jint.Tests.Runtime.ExtensionMethods; + +internal class Subscribe : IObserver { - internal class Subscribe : IObserver - { - private readonly Action onNext; - private readonly Action onError; - private readonly Action onCompleted; + private readonly Action onNext; + private readonly Action onError; + private readonly Action onCompleted; - readonly int isStopped = 0; + readonly int isStopped = 0; - public Subscribe(Action onNext, Action onError, Action onCompleted) - { - this.onNext = onNext; - this.onError = onError; - this.onCompleted = onCompleted; - } + public Subscribe(Action onNext, Action onError, Action onCompleted) + { + this.onNext = onNext; + this.onError = onError; + this.onCompleted = onCompleted; + } - public void OnNext(T value) + public void OnNext(T value) + { + if (isStopped == 0) { - if (isStopped == 0) - { - onNext(value); - } + onNext(value); } + } - public void OnError(Exception error) - { - onError(error); - } + public void OnError(Exception error) + { + onError(error); + } - public void OnCompleted() - { - onCompleted(); - } + public void OnCompleted() + { + onCompleted(); } +} - public static partial class ObservableExtensions +public static partial class ObservableExtensions +{ + public static void Subscribe(this IObservable source, Action onNext) { - public static void Subscribe(this IObservable source, Action onNext) - { - var subs = new Subscribe(onNext, null, null); - source.Subscribe(subs); - } - - public static TResult Select(this IObservable source, TResult result) - { - return result; - } + var subs = new Subscribe(onNext, null, null); + source.Subscribe(subs); + } - public static IObservable Where(this IObservable source, Func predicate) - { - T t = default; - predicate(t); - return source; - } + public static TResult Select(this IObservable source, TResult result) + { + return result; + } - public static IObservable Where(this IObservable source, Func predicate) - { - T t = default; - bool result = predicate(t, 42); - return source; - } + public static IObservable Where(this IObservable source, Func predicate) + { + T t = default; + predicate(t); + return source; } - public class BaseObservable : IObservable + public static IObservable Where(this IObservable source, Func predicate) { - private readonly List> observers = new List>(); + T t = default; + bool result = predicate(t, 42); + return source; + } +} - public T Last { get; private set; } +public class BaseObservable : IObservable +{ + private readonly List> observers = new List>(); - public IDisposable Subscribe(IObserver observer) - { - if (!observers.Contains(observer)) - observers.Add(observer); - return new Unsubscriber(observers, observer); - } + public T Last { get; private set; } - private class Unsubscriber : IDisposable - { - private readonly List> _observers; - private readonly IObserver _observer; - - public Unsubscriber(List> observers, IObserver observer) - { - this._observers = observers; - this._observer = observer; - } - - public void Dispose() - { - if (_observer != null && _observers.Contains(_observer)) - _observers.Remove(_observer); - } - } + public IDisposable Subscribe(IObserver observer) + { + if (!observers.Contains(observer)) + observers.Add(observer); + return new Unsubscriber(observers, observer); + } - protected void BroadcastUpdate(T t) - { - foreach (var observer in observers) - { - observer.OnNext(t); - } - } + private class Unsubscriber : IDisposable + { + private readonly List> _observers; + private readonly IObserver _observer; - public void Update(T t) + public Unsubscriber(List> observers, IObserver observer) { - Last = t; - BroadcastUpdate(t); + this._observers = observers; + this._observer = observer; } - public void BroadcastCompleted() + public void Dispose() { - foreach (var observer in observers.ToArray()) - { - observer.OnCompleted(); - } - observers.Clear(); + if (_observer != null && _observers.Contains(_observer)) + _observers.Remove(_observer); } } - public class ObservableFactory + protected void BroadcastUpdate(T t) { - public static BaseObservable GetBoolBaseObservable() + foreach (var observer in observers) { - return new BaseObservable(); + observer.OnNext(t); } } - public class NameObservable : BaseObservable + public void Update(T t) { - public void UpdateName(string name) - { - Update(name); - } + Last = t; + BroadcastUpdate(t); + } - public void CommitName() + public void BroadcastCompleted() + { + foreach (var observer in observers.ToArray()) { - BroadcastCompleted(); + observer.OnCompleted(); } + observers.Clear(); + } +} + +public class ObservableFactory +{ + public static BaseObservable GetBoolBaseObservable() + { + return new BaseObservable(); } } + +public class NameObservable : BaseObservable +{ + public void UpdateName(string name) + { + Update(name); + } + + public void CommitName() + { + BroadcastCompleted(); + } +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/ExtensionMethods/OverrideStringPrototypeExtensions.cs b/Jint.Tests/Runtime/ExtensionMethods/OverrideStringPrototypeExtensions.cs index dfd3c76ddf..852d7f9c0f 100644 --- a/Jint.Tests/Runtime/ExtensionMethods/OverrideStringPrototypeExtensions.cs +++ b/Jint.Tests/Runtime/ExtensionMethods/OverrideStringPrototypeExtensions.cs @@ -1,10 +1,9 @@ -namespace Jint.Tests.Runtime.ExtensionMethods +namespace Jint.Tests.Runtime.ExtensionMethods; + +public static class OverrideStringPrototypeExtensions { - public static class OverrideStringPrototypeExtensions + public static string[] Split(this string value, string delimiter) { - public static string[] Split(this string value, string delimiter) - { - return value.Split(delimiter.ToCharArray()).Select(v => v.ToUpper()).ToArray(); - } + return value.Split(delimiter.ToCharArray()).Select(v => v.ToUpper()).ToArray(); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/ExtensionMethods/PersonExtensions.cs b/Jint.Tests/Runtime/ExtensionMethods/PersonExtensions.cs index ef4f17e465..d3c2a7300b 100644 --- a/Jint.Tests/Runtime/ExtensionMethods/PersonExtensions.cs +++ b/Jint.Tests/Runtime/ExtensionMethods/PersonExtensions.cs @@ -1,12 +1,11 @@ using Jint.Tests.Runtime.Domain; -namespace Jint.Tests.Runtime.ExtensionMethods +namespace Jint.Tests.Runtime.ExtensionMethods; + +public static class PersonExtensions { - public static class PersonExtensions + public static int MultiplyAge(this Person person, int factor) { - public static int MultiplyAge(this Person person, int factor) - { - return person.Age * factor; - } + return person.Age * factor; } } \ No newline at end of file diff --git a/Jint.Tests/Runtime/GlobalTests.cs b/Jint.Tests/Runtime/GlobalTests.cs index 6885db9768..d2c9cceec3 100644 --- a/Jint.Tests/Runtime/GlobalTests.cs +++ b/Jint.Tests/Runtime/GlobalTests.cs @@ -1,17 +1,16 @@ -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class GlobalTests { - public class GlobalTests + [Fact] + public void UnescapeAtEndOfString() { - [Fact] - public void UnescapeAtEndOfString() - { - var e = new Engine(); + var e = new Engine(); - Assert.Equal("@", e.Evaluate("unescape('%40');").AsString()); - Assert.Equal("@_", e.Evaluate("unescape('%40_');").AsString()); - Assert.Equal("@@", e.Evaluate("unescape('%40%40');").AsString()); - Assert.Equal("@", e.Evaluate("unescape('%u0040');").AsString()); - Assert.Equal("@@", e.Evaluate("unescape('%u0040%u0040');").AsString()); - } + Assert.Equal("@", e.Evaluate("unescape('%40');").AsString()); + Assert.Equal("@_", e.Evaluate("unescape('%40_');").AsString()); + Assert.Equal("@@", e.Evaluate("unescape('%40%40');").AsString()); + Assert.Equal("@", e.Evaluate("unescape('%u0040');").AsString()); + Assert.Equal("@@", e.Evaluate("unescape('%u0040%u0040');").AsString()); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/InteropTests.MemberAccess.cs b/Jint.Tests/Runtime/InteropTests.MemberAccess.cs index f78d77d7f8..aaba07b3b9 100644 --- a/Jint.Tests/Runtime/InteropTests.MemberAccess.cs +++ b/Jint.Tests/Runtime/InteropTests.MemberAccess.cs @@ -5,280 +5,279 @@ using Jint.Runtime.Interop; using Jint.Tests.Runtime.Domain; -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public partial class InteropTests { - public partial class InteropTests + [Fact] + public void ShouldHideSpecificMembers() { - [Fact] - public void ShouldHideSpecificMembers() + var engine = new Engine(options => options.SetMemberAccessor((e, target, member) => { - var engine = new Engine(options => options.SetMemberAccessor((e, target, member) => + if (target is HiddenMembers) { - if (target is HiddenMembers) + if (member == nameof(HiddenMembers.Member2) || member == nameof(HiddenMembers.Method2)) { - if (member == nameof(HiddenMembers.Member2) || member == nameof(HiddenMembers.Method2)) - { - return JsValue.Undefined; - } + return JsValue.Undefined; } + } - return null; - })); + return null; + })); - engine.SetValue("m", new HiddenMembers()); + engine.SetValue("m", new HiddenMembers()); - Assert.Equal("Member1", engine.Evaluate("m.Member1").ToString()); - Assert.Equal("undefined", engine.Evaluate("m.Member2").ToString()); - Assert.Equal("Method1", engine.Evaluate("m.Method1()").ToString()); - // check the method itself, not its invokation as it would mean invoking "undefined" - Assert.Equal("undefined", engine.Evaluate("m.Method2").ToString()); - } + Assert.Equal("Member1", engine.Evaluate("m.Member1").ToString()); + Assert.Equal("undefined", engine.Evaluate("m.Member2").ToString()); + Assert.Equal("Method1", engine.Evaluate("m.Method1()").ToString()); + // check the method itself, not its invokation as it would mean invoking "undefined" + Assert.Equal("undefined", engine.Evaluate("m.Method2").ToString()); + } - [Fact] - public void ShouldOverrideMembers() + [Fact] + public void ShouldOverrideMembers() + { + var engine = new Engine(options => options.SetMemberAccessor((e, target, member) => { - var engine = new Engine(options => options.SetMemberAccessor((e, target, member) => + if (target is HiddenMembers && member == nameof(HiddenMembers.Member1)) { - if (target is HiddenMembers && member == nameof(HiddenMembers.Member1)) - { - return "Orange"; - } + return "Orange"; + } - return null; - })); + return null; + })); - engine.SetValue("m", new HiddenMembers()); + engine.SetValue("m", new HiddenMembers()); - Assert.Equal("Orange", engine.Evaluate("m.Member1").ToString()); - } + Assert.Equal("Orange", engine.Evaluate("m.Member1").ToString()); + } - [Fact] - public void ShouldBeAbleToFilterMembers() - { - var engine = new Engine(options => options - .SetTypeResolver(new TypeResolver - { - MemberFilter = member => !Attribute.IsDefined(member, typeof(ObsoleteAttribute)) - }) - ); + [Fact] + public void ShouldBeAbleToFilterMembers() + { + var engine = new Engine(options => options + .SetTypeResolver(new TypeResolver + { + MemberFilter = member => !Attribute.IsDefined(member, typeof(ObsoleteAttribute)) + }) + ); - engine.SetValue("m", new HiddenMembers()); + engine.SetValue("m", new HiddenMembers()); - Assert.True(engine.Evaluate("m.Field1").IsUndefined()); - Assert.True(engine.Evaluate("m.Member1").IsUndefined()); - Assert.True(engine.Evaluate("m.Method1").IsUndefined()); + Assert.True(engine.Evaluate("m.Field1").IsUndefined()); + Assert.True(engine.Evaluate("m.Member1").IsUndefined()); + Assert.True(engine.Evaluate("m.Method1").IsUndefined()); - Assert.True(engine.Evaluate("m.Field2").IsString()); - Assert.True(engine.Evaluate("m.Member2").IsString()); - Assert.True(engine.Evaluate("m.Method2()").IsString()); + Assert.True(engine.Evaluate("m.Field2").IsString()); + Assert.True(engine.Evaluate("m.Member2").IsString()); + Assert.True(engine.Evaluate("m.Method2()").IsString()); - // we forbid GetType by default - Assert.True(engine.Evaluate("m.GetType").IsUndefined()); - } + // we forbid GetType by default + Assert.True(engine.Evaluate("m.GetType").IsUndefined()); + } - [Fact] - public void ShouldBeAbleToExposeGetType() + [Fact] + public void ShouldBeAbleToExposeGetType() + { + var engine = new Engine(options => { - var engine = new Engine(options => - { - options.Interop.AllowGetType = true; - options.Interop.AllowSystemReflection = true; - }); - engine.SetValue("m", new HiddenMembers()); - Assert.True(engine.Evaluate("m.GetType").IsCallable); - - // reflection could bypass some safeguards - Assert.Equal("Method1", engine.Evaluate("m.GetType().GetMethod('Method1').Invoke(m, [])").AsString()); - } + options.Interop.AllowGetType = true; + options.Interop.AllowSystemReflection = true; + }); + engine.SetValue("m", new HiddenMembers()); + Assert.True(engine.Evaluate("m.GetType").IsCallable); + + // reflection could bypass some safeguards + Assert.Equal("Method1", engine.Evaluate("m.GetType().GetMethod('Method1').Invoke(m, [])").AsString()); + } - [Fact] - public void ShouldBeAbleToVaryGetTypeConfigurationBetweenEngines() + [Fact] + public void ShouldBeAbleToVaryGetTypeConfigurationBetweenEngines() + { + static string TestAllowGetTypeOption(bool allowGetType) { - static string TestAllowGetTypeOption(bool allowGetType) - { - var uri = new Uri("https://github.com/sebastienros/jint"); - const string Input = nameof(uri) + ".GetType();"; + var uri = new Uri("https://github.com/sebastienros/jint"); + const string Input = nameof(uri) + ".GetType();"; - using var engine = new Engine(options => options.Interop.AllowGetType = allowGetType); - engine.SetValue(nameof(uri), JsValue.FromObject(engine, uri)); - var result = engine.Evaluate(Input).ToString(); - return result; - } + using var engine = new Engine(options => options.Interop.AllowGetType = allowGetType); + engine.SetValue(nameof(uri), JsValue.FromObject(engine, uri)); + var result = engine.Evaluate(Input).ToString(); + return result; + } - Assert.Equal("System.Uri", TestAllowGetTypeOption(allowGetType: true)); + Assert.Equal("System.Uri", TestAllowGetTypeOption(allowGetType: true)); - var ex = Assert.Throws(() => TestAllowGetTypeOption(allowGetType: false)); - Assert.Equal("Property 'GetType' of object is not a function", ex.Message); - } + var ex = Assert.Throws(() => TestAllowGetTypeOption(allowGetType: false)); + Assert.Equal("Property 'GetType' of object is not a function", ex.Message); + } - [Fact] - public void ShouldProtectFromReflectionServiceUsage() - { - var engine = new Engine(); - engine.SetValue("m", new HiddenMembers()); + [Fact] + public void ShouldProtectFromReflectionServiceUsage() + { + var engine = new Engine(); + engine.SetValue("m", new HiddenMembers()); - // we can get a type reference if it's exposed via property, bypassing GetType - var type = engine.Evaluate("m.Type"); - Assert.IsType(type); + // we can get a type reference if it's exposed via property, bypassing GetType + var type = engine.Evaluate("m.Type"); + Assert.IsType(type); - var ex = Assert.Throws(() => engine.Evaluate("m.Type.Module.GetType().Module.GetType('System.DateTime')")); - Assert.Equal("Cannot access System.Reflection namespace, check Engine's interop options", ex.Message); - } + var ex = Assert.Throws(() => engine.Evaluate("m.Type.Module.GetType().Module.GetType('System.DateTime')")); + Assert.Equal("Cannot access System.Reflection namespace, check Engine's interop options", ex.Message); + } - [Fact] - public void TypeReferenceShouldUseTypeResolverConfiguration() + [Fact] + public void TypeReferenceShouldUseTypeResolverConfiguration() + { + var engine = new Engine(options => { - var engine = new Engine(options => + options.SetTypeResolver(new TypeResolver { - options.SetTypeResolver(new TypeResolver - { - MemberFilter = member => !Attribute.IsDefined(member, typeof(ObsoleteAttribute)) - }); + MemberFilter = member => !Attribute.IsDefined(member, typeof(ObsoleteAttribute)) }); - engine.SetValue("EchoService", TypeReference.CreateTypeReference(engine, typeof(EchoService))); - Assert.Equal("anyone there", engine.Evaluate("EchoService.Echo('anyone there')").AsString()); - Assert.Equal("anyone there", engine.Evaluate("EchoService.echo('anyone there')").AsString()); - Assert.True(engine.Evaluate("EchoService.ECHO").IsUndefined()); + }); + engine.SetValue("EchoService", TypeReference.CreateTypeReference(engine, typeof(EchoService))); + Assert.Equal("anyone there", engine.Evaluate("EchoService.Echo('anyone there')").AsString()); + Assert.Equal("anyone there", engine.Evaluate("EchoService.echo('anyone there')").AsString()); + Assert.True(engine.Evaluate("EchoService.ECHO").IsUndefined()); - Assert.True(engine.Evaluate("EchoService.Hidden").IsUndefined()); - } + Assert.True(engine.Evaluate("EchoService.Hidden").IsUndefined()); + } - [Fact] - public void CustomDictionaryPropertyAccessTests() + [Fact] + public void CustomDictionaryPropertyAccessTests() + { + var engine = new Engine(options => { - var engine = new Engine(options => - { - options.AllowClr(); - }); + options.AllowClr(); + }); - var dc = new CustomDictionary(); - dc["a"] = 1; + var dc = new CustomDictionary(); + dc["a"] = 1; - engine.SetValue("dc", dc); + engine.SetValue("dc", dc); - Assert.Equal(1, engine.Evaluate("dc.a")); - Assert.Equal(1, engine.Evaluate("dc['a']")); - Assert.NotEqual(JsValue.Undefined, engine.Evaluate("dc.Add")); - Assert.NotEqual(0, engine.Evaluate("dc.Add")); - Assert.Equal(JsValue.Undefined, engine.Evaluate("dc.b")); + Assert.Equal(1, engine.Evaluate("dc.a")); + Assert.Equal(1, engine.Evaluate("dc['a']")); + Assert.NotEqual(JsValue.Undefined, engine.Evaluate("dc.Add")); + Assert.NotEqual(0, engine.Evaluate("dc.Add")); + Assert.Equal(JsValue.Undefined, engine.Evaluate("dc.b")); - engine.Execute("dc.b = 5"); - engine.Execute("dc.Add('c', 10)"); - Assert.Equal(5, engine.Evaluate("dc.b")); - Assert.Equal(10, engine.Evaluate("dc.c")); - } + engine.Execute("dc.b = 5"); + engine.Execute("dc.Add('c', 10)"); + Assert.Equal(5, engine.Evaluate("dc.b")); + Assert.Equal(10, engine.Evaluate("dc.c")); + } - [Fact] - public void CanAccessBaseClassStaticFields() + [Fact] + public void CanAccessBaseClassStaticFields() + { + var engine = new Engine(options => { - var engine = new Engine(options => - { - options.AllowClr(); - }); + options.AllowClr(); + }); - engine.SetValue("B", TypeReference.CreateTypeReference(engine, typeof(InheritingFromClassWithStatics))); - Assert.Equal(42, engine.Evaluate("B.a").AsNumber()); - Assert.Equal(24, engine.Evaluate("B.a = 24; B.a").AsNumber()); - } + engine.SetValue("B", TypeReference.CreateTypeReference(engine, typeof(InheritingFromClassWithStatics))); + Assert.Equal(42, engine.Evaluate("B.a").AsNumber()); + Assert.Equal(24, engine.Evaluate("B.a = 24; B.a").AsNumber()); + } - private class BaseClassWithStatics - { + private class BaseClassWithStatics + { #pragma warning disable CS0414 // Field is assigned but its value is never used - public static int a = 42; + public static int a = 42; #pragma warning restore CS0414 // Field is assigned but its value is never used - } + } - private class InheritingFromClassWithStatics : BaseClassWithStatics - { - } + private class InheritingFromClassWithStatics : BaseClassWithStatics + { + } - private static class EchoService - { - public static string Echo(string message) => message; + private static class EchoService + { + public static string Echo(string message) => message; - [Obsolete] - public static string Hidden(string message) => message; - } + [Obsolete] + public static string Hidden(string message) => message; + } - private class CustomDictionary : IDictionary - { - readonly Dictionary _dictionary = new Dictionary(); + private class CustomDictionary : IDictionary + { + readonly Dictionary _dictionary = new Dictionary(); - public TValue this[string key] + public TValue this[string key] + { + get { - get - { - if (TryGetValue(key, out var val)) return val; + if (TryGetValue(key, out var val)) return val; - // Normally, dictionary Item accessor throws an error when key does not exist - // But this is a custom implementation - return default; - } - set - { - _dictionary[key] = value; - } + // Normally, dictionary Item accessor throws an error when key does not exist + // But this is a custom implementation + return default; + } + set + { + _dictionary[key] = value; } - - public ICollection Keys => _dictionary.Keys; - public ICollection Values => _dictionary.Values; - public int Count => _dictionary.Count; - public bool IsReadOnly => false; - public void Add(string key, TValue value) => _dictionary.Add(key, value); - public void Add(KeyValuePair item) => _dictionary.Add(item.Key, item.Value); - public void Clear() => _dictionary.Clear(); - public bool Contains(KeyValuePair item) => _dictionary.ContainsKey(item.Key); - public bool ContainsKey(string key) => _dictionary.ContainsKey(key); - public void CopyTo(KeyValuePair[] array, int arrayIndex) => throw new NotImplementedException(); - public IEnumerator> GetEnumerator() => _dictionary.GetEnumerator(); - public bool Remove(string key) => _dictionary.Remove(key); - public bool Remove(KeyValuePair item) => _dictionary.Remove(item.Key); - public bool TryGetValue(string key, [MaybeNullWhen(false)] out TValue value) => _dictionary.TryGetValue(key, out value); - IEnumerator IEnumerable.GetEnumerator() => _dictionary.GetEnumerator(); } - public class ClassWithData - { - public int Age => 42; + public ICollection Keys => _dictionary.Keys; + public ICollection Values => _dictionary.Values; + public int Count => _dictionary.Count; + public bool IsReadOnly => false; + public void Add(string key, TValue value) => _dictionary.Add(key, value); + public void Add(KeyValuePair item) => _dictionary.Add(item.Key, item.Value); + public void Clear() => _dictionary.Clear(); + public bool Contains(KeyValuePair item) => _dictionary.ContainsKey(item.Key); + public bool ContainsKey(string key) => _dictionary.ContainsKey(key); + public void CopyTo(KeyValuePair[] array, int arrayIndex) => throw new NotImplementedException(); + public IEnumerator> GetEnumerator() => _dictionary.GetEnumerator(); + public bool Remove(string key) => _dictionary.Remove(key); + public bool Remove(KeyValuePair item) => _dictionary.Remove(item.Key); + public bool TryGetValue(string key, [MaybeNullWhen(false)] out TValue value) => _dictionary.TryGetValue(key, out value); + IEnumerator IEnumerable.GetEnumerator() => _dictionary.GetEnumerator(); + } - public DataType Data { get; set; } + public class ClassWithData + { + public int Age => 42; - public class DataType - { - public string Value { get; set; } - } - } + public DataType Data { get; set; } - [Fact] - public void NewTypedObjectFromUntypedInitializerShouldBeMapped() + public class DataType { - var engine = new Engine(); + public string Value { get; set; } + } + } - engine.SetValue("obj", new ClassWithData()); - engine.Execute("obj.Data = { Value: '123' };"); - var obj = engine.Evaluate("obj").ToObject() as ClassWithData; + [Fact] + public void NewTypedObjectFromUntypedInitializerShouldBeMapped() + { + var engine = new Engine(); - Assert.Equal("123", obj?.Data.Value); - } + engine.SetValue("obj", new ClassWithData()); + engine.Execute("obj.Data = { Value: '123' };"); + var obj = engine.Evaluate("obj").ToObject() as ClassWithData; - [Fact] - public void CanConfigureStrictAccess() - { - var engine = new Engine(); + Assert.Equal("123", obj?.Data.Value); + } + + [Fact] + public void CanConfigureStrictAccess() + { + var engine = new Engine(); - engine.SetValue("obj", new ClassWithData()); - engine.Evaluate("obj.Age").AsNumber().Should().Be(42); - engine.Evaluate("obj.AgeMissing").Should().Be(JsValue.Undefined); + engine.SetValue("obj", new ClassWithData()); + engine.Evaluate("obj.Age").AsNumber().Should().Be(42); + engine.Evaluate("obj.AgeMissing").Should().Be(JsValue.Undefined); - engine = new Engine(options => - { - options.Interop.ThrowOnUnresolvedMember = true; - }); + engine = new Engine(options => + { + options.Interop.ThrowOnUnresolvedMember = true; + }); - engine.SetValue("obj", new ClassWithData()); - engine.Evaluate("obj.Age").AsNumber().Should().Be(42); + engine.SetValue("obj", new ClassWithData()); + engine.Evaluate("obj.Age").AsNumber().Should().Be(42); - engine.Invoking(e => e.Evaluate("obj.AgeMissing")).Should().Throw(); - } + engine.Invoking(e => e.Evaluate("obj.AgeMissing")).Should().Throw(); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/InteropTests.cs b/Jint.Tests/Runtime/InteropTests.cs index 8b6eb68a0b..cf83edd67e 100644 --- a/Jint.Tests/Runtime/InteropTests.cs +++ b/Jint.Tests/Runtime/InteropTests.cs @@ -11,127 +11,127 @@ using MongoDB.Bson; using Shapes; -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public partial class InteropTests : IDisposable { - public partial class InteropTests : IDisposable + private readonly Engine _engine; + + public InteropTests() { - private readonly Engine _engine; + _engine = new Engine(cfg => cfg.AllowClr( + typeof(Shape).GetTypeInfo().Assembly, + typeof(Console).GetTypeInfo().Assembly, + typeof(File).GetTypeInfo().Assembly)) + .SetValue("log", new Action(Console.WriteLine)) + .SetValue("assert", new Action(Assert.True)) + .SetValue("equal", new Action(Assert.Equal)) + ; + } - public InteropTests() - { - _engine = new Engine(cfg => cfg.AllowClr( - typeof(Shape).GetTypeInfo().Assembly, - typeof(Console).GetTypeInfo().Assembly, - typeof(File).GetTypeInfo().Assembly)) - .SetValue("log", new Action(Console.WriteLine)) - .SetValue("assert", new Action(Assert.True)) - .SetValue("equal", new Action(Assert.Equal)) - ; - } + void IDisposable.Dispose() + { + } - void IDisposable.Dispose() - { - } + private void RunTest(string source) + { + _engine.Execute(source); + } - private void RunTest(string source) + public class Foo + { + public static Bar GetBar() { - _engine.Execute(source); + return new Bar(); } + } - public class Foo - { - public static Bar GetBar() - { - return new Bar(); - } - } + public class Bar + { + public string Test { get; set; } = "123"; + } - public class Bar - { - public string Test { get; set; } = "123"; - } + [Fact] + public void ShouldStringifyNetObjects() + { + _engine.SetValue("foo", new Foo()); + var json = _engine.Evaluate("JSON.stringify(foo.GetBar())").AsString(); + Assert.Equal("{\"Test\":\"123\"}", json); + } - [Fact] - public void ShouldStringifyNetObjects() - { - _engine.SetValue("foo", new Foo()); - var json = _engine.Evaluate("JSON.stringify(foo.GetBar())").AsString(); - Assert.Equal("{\"Test\":\"123\"}", json); - } + [Fact] + public void EngineShouldStringifyADictionary() + { + var engine = new Engine(); - [Fact] - public void EngineShouldStringifyADictionary() - { - var engine = new Engine(); + var d = new Hashtable(); + d["Values"] = 1; + engine.SetValue("d", d); - var d = new Hashtable(); - d["Values"] = 1; - engine.SetValue("d", d); + Assert.Equal("{\"Values\":1}", engine.Evaluate($"JSON.stringify(d)").AsString()); + } - Assert.Equal("{\"Values\":1}", engine.Evaluate($"JSON.stringify(d)").AsString()); - } + [Fact] + public void EngineShouldStringifyADictionaryOfStringAndObjectCorrectly() + { + var engine = new Engine(); - [Fact] - public void EngineShouldStringifyADictionaryOfStringAndObjectCorrectly() + var dictionary = new Dictionary { - var engine = new Engine(); - - var dictionary = new Dictionary - { - { "foo", 5 }, - { "bar", "A string" } - }; - engine.SetValue(nameof(dictionary), dictionary); + { "foo", 5 }, + { "bar", "A string" } + }; + engine.SetValue(nameof(dictionary), dictionary); - var result = engine.Evaluate($"JSON.stringify({nameof(dictionary)})").AsString(); - Assert.Equal("{\"foo\":5,\"bar\":\"A string\"}", result); - } + var result = engine.Evaluate($"JSON.stringify({nameof(dictionary)})").AsString(); + Assert.Equal("{\"foo\":5,\"bar\":\"A string\"}", result); + } - [Fact] - public void EngineShouldRoundtripParsedJSONBackToStringCorrectly() - { - var engine = new Engine(); + [Fact] + public void EngineShouldRoundtripParsedJSONBackToStringCorrectly() + { + var engine = new Engine(); - const string json = "{\"foo\":5,\"bar\":\"A string\"}"; - var parsed = engine.Evaluate($"JSON.parse('{json}')").ToObject(); - engine.SetValue(nameof(parsed), parsed); + const string json = "{\"foo\":5,\"bar\":\"A string\"}"; + var parsed = engine.Evaluate($"JSON.parse('{json}')").ToObject(); + engine.SetValue(nameof(parsed), parsed); - var result = engine.Evaluate($"JSON.stringify({nameof(parsed)})").AsString(); - Assert.Equal(json, result); - } + var result = engine.Evaluate($"JSON.stringify({nameof(parsed)})").AsString(); + Assert.Equal(json, result); + } - [Fact] - public void PrimitiveTypesCanBeSet() - { - _engine.SetValue("x", 10); - _engine.SetValue("y", true); - _engine.SetValue("z", "foo"); + [Fact] + public void PrimitiveTypesCanBeSet() + { + _engine.SetValue("x", 10); + _engine.SetValue("y", true); + _engine.SetValue("z", "foo"); - RunTest(@" + RunTest(@" assert(x === 10); assert(y === true); assert(z === 'foo'); "); - } + } - [Fact] - public void TypePropertyAccess() - { - var userClass = new Person(); + [Fact] + public void TypePropertyAccess() + { + var userClass = new Person(); - var result = new Engine() - .SetValue("userclass", userClass) - .Evaluate("userclass.TypeProperty.Name;") - .AsString(); + var result = new Engine() + .SetValue("userclass", userClass) + .Evaluate("userclass.TypeProperty.Name;") + .AsString(); - Assert.Equal("Person", result); - } + Assert.Equal("Person", result); + } - [Fact] - public void CanAccessMemberNamedItem() - { - _engine.Execute(@" + [Fact] + public void CanAccessMemberNamedItem() + { + _engine.Execute(@" function item2(arg) { return arg.item2 } @@ -143,136 +143,136 @@ function item3(arg) { } "); - var argument = new Dictionary - { - { "item2", "item2 value" }, - { "item", "item value" }, - { "Item", "Item value" } - }; - - Assert.Equal("item2 value", _engine.Invoke("item2", argument)); - Assert.Equal("item value", _engine.Invoke("item1", argument)); - Assert.Equal("Item value", _engine.Invoke("item3", argument)); - - var company = new Company("Acme Ltd"); - _engine.SetValue("c", company); - Assert.Equal("item thingie", _engine.Evaluate("c.Item")); - Assert.Equal("item thingie", _engine.Evaluate("c.item")); - Assert.Equal("value", _engine.Evaluate("c['key']")); - } - - [Fact] - public void DelegatesCanBeSet() + var argument = new Dictionary { - _engine.SetValue("square", new Func(x => x * x)); + { "item2", "item2 value" }, + { "item", "item value" }, + { "Item", "Item value" } + }; + + Assert.Equal("item2 value", _engine.Invoke("item2", argument)); + Assert.Equal("item value", _engine.Invoke("item1", argument)); + Assert.Equal("Item value", _engine.Invoke("item3", argument)); + + var company = new Company("Acme Ltd"); + _engine.SetValue("c", company); + Assert.Equal("item thingie", _engine.Evaluate("c.Item")); + Assert.Equal("item thingie", _engine.Evaluate("c.item")); + Assert.Equal("value", _engine.Evaluate("c['key']")); + } + + [Fact] + public void DelegatesCanBeSet() + { + _engine.SetValue("square", new Func(x => x * x)); - RunTest(@" + RunTest(@" assert(square(10) === 100); "); - } + } - [Fact] - public void DelegateWithNullableParameterCanBePassedANull() - { - _engine.SetValue("isnull", new Func(x => x == null)); + [Fact] + public void DelegateWithNullableParameterCanBePassedANull() + { + _engine.SetValue("isnull", new Func(x => x == null)); - RunTest(@" + RunTest(@" assert(isnull(null) === true); "); - } + } - [Fact] - public void DelegateWithObjectParameterCanBePassedANull() - { - _engine.SetValue("isnull", new Func(x => x == null)); + [Fact] + public void DelegateWithObjectParameterCanBePassedANull() + { + _engine.SetValue("isnull", new Func(x => x == null)); - RunTest(@" + RunTest(@" assert(isnull(null) === true); "); - } + } - [Fact] - public void DelegateWithNullableParameterCanBePassedAnUndefined() - { - _engine.SetValue("isnull", new Func(x => x == null)); + [Fact] + public void DelegateWithNullableParameterCanBePassedAnUndefined() + { + _engine.SetValue("isnull", new Func(x => x == null)); - RunTest(@" + RunTest(@" assert(isnull(undefined) === true); "); - } + } - [Fact] - public void DelegateWithObjectParameterCanBePassedAnUndefined() - { - _engine.SetValue("isnull", new Func(x => x == null)); + [Fact] + public void DelegateWithObjectParameterCanBePassedAnUndefined() + { + _engine.SetValue("isnull", new Func(x => x == null)); - RunTest(@" + RunTest(@" assert(isnull(undefined) === true); "); - } + } - [Fact] - public void DelegateWithNullableParameterCanBeExcluded() - { - _engine.SetValue("isnull", new Func(x => x == null)); + [Fact] + public void DelegateWithNullableParameterCanBeExcluded() + { + _engine.SetValue("isnull", new Func(x => x == null)); - RunTest(@" + RunTest(@" assert(isnull() === true); "); - } + } - [Fact] - public void DelegateWithObjectParameterCanBeExcluded() - { - _engine.SetValue("isnull", new Func(x => x == null)); + [Fact] + public void DelegateWithObjectParameterCanBeExcluded() + { + _engine.SetValue("isnull", new Func(x => x == null)); - RunTest(@" + RunTest(@" assert(isnull() === true); "); - } + } - [Fact] - public void DynamicDelegateCanBeSet() - { + [Fact] + public void DynamicDelegateCanBeSet() + { #if NETFRAMEWORK - var parameters = new[] - { - System.Linq.Expressions.Expression.Parameter(typeof(int)), - System.Linq.Expressions.Expression.Parameter(typeof(int)) - }; - var exp = System.Linq.Expressions.Expression.Add(parameters[0], parameters[1]); - var del = System.Linq.Expressions.Expression.Lambda(exp, parameters).Compile(); + var parameters = new[] + { + System.Linq.Expressions.Expression.Parameter(typeof(int)), + System.Linq.Expressions.Expression.Parameter(typeof(int)) + }; + var exp = System.Linq.Expressions.Expression.Add(parameters[0], parameters[1]); + var del = System.Linq.Expressions.Expression.Lambda(exp, parameters).Compile(); - _engine.SetValue("add", del); + _engine.SetValue("add", del); - RunTest(@" + RunTest(@" assert(add(1,1) === 2); "); #endif - } + } - [Fact] - public void ExtraParametersAreIgnored() - { - _engine.SetValue("passNumber", new Func(x => x)); + [Fact] + public void ExtraParametersAreIgnored() + { + _engine.SetValue("passNumber", new Func(x => x)); - RunTest(@" + RunTest(@" assert(passNumber(123,'test',{},[],null) === 123); "); - } + } - private delegate string callParams(params object[] values); + private delegate string callParams(params object[] values); - private delegate string callArgumentAndParams(string firstParam, params object[] values); + private delegate string callArgumentAndParams(string firstParam, params object[] values); - [Fact] - public void DelegatesWithParamsParameterCanBeInvoked() - { - var a = new A(); - _engine.SetValue("callParams", new callParams(a.Call13)); - _engine.SetValue("callArgumentAndParams", new callArgumentAndParams(a.Call14)); + [Fact] + public void DelegatesWithParamsParameterCanBeInvoked() + { + var a = new A(); + _engine.SetValue("callParams", new callParams(a.Call13)); + _engine.SetValue("callArgumentAndParams", new callArgumentAndParams(a.Call14)); - RunTest(@" + RunTest(@" assert(callParams('1','2','3') === '1,2,3'); assert(callParams('1') === '1'); assert(callParams() === ''); @@ -282,528 +282,528 @@ public void DelegatesWithParamsParameterCanBeInvoked() assert(callArgumentAndParams('a') === 'a:'); assert(callArgumentAndParams() === ':'); "); - } + } - [Fact] - public void CanGetObjectProperties() + [Fact] + public void CanGetObjectProperties() + { + var p = new Person { - var p = new Person - { - Name = "Mickey Mouse" - }; + Name = "Mickey Mouse" + }; - _engine.SetValue("p", p); + _engine.SetValue("p", p); - RunTest(@" + RunTest(@" assert(p.Name === 'Mickey Mouse'); "); - } + } - [Fact] - public void CanInvokeObjectMethods() + [Fact] + public void CanInvokeObjectMethods() + { + var p = new Person { - var p = new Person - { - Name = "Mickey Mouse" - }; + Name = "Mickey Mouse" + }; - _engine.SetValue("p", p); + _engine.SetValue("p", p); - RunTest(@" + RunTest(@" assert(p.ToString() === 'Mickey Mouse'); "); - } + } - [Fact] - public void CanInvokeObjectMethodsWithPascalCase() + [Fact] + public void CanInvokeObjectMethodsWithPascalCase() + { + var p = new Person { - var p = new Person - { - Name = "Mickey Mouse" - }; + Name = "Mickey Mouse" + }; - _engine.SetValue("p", p); + _engine.SetValue("p", p); - RunTest(@" + RunTest(@" assert(p.toString() === 'Mickey Mouse'); "); - } + } - [Fact] - public void CanSetObjectProperties() + [Fact] + public void CanSetObjectProperties() + { + var p = new Person { - var p = new Person - { - Name = "Mickey Mouse" - }; + Name = "Mickey Mouse" + }; - _engine.SetValue("p", p); + _engine.SetValue("p", p); - RunTest(@" + RunTest(@" p.Name = 'Donald Duck'; assert(p.Name === 'Donald Duck'); "); - Assert.Equal("Donald Duck", p.Name); - } + Assert.Equal("Donald Duck", p.Name); + } - [Fact] - public void CanGetIndexUsingStringKey() - { - var dictionary = new Dictionary(); - dictionary.Add("person1", new Person { Name = "Mickey Mouse" }); - dictionary.Add("person2", new Person { Name = "Goofy" }); + [Fact] + public void CanGetIndexUsingStringKey() + { + var dictionary = new Dictionary(); + dictionary.Add("person1", new Person { Name = "Mickey Mouse" }); + dictionary.Add("person2", new Person { Name = "Goofy" }); - _engine.SetValue("dictionary", dictionary); + _engine.SetValue("dictionary", dictionary); - RunTest(@" + RunTest(@" assert(dictionary['person1'].Name === 'Mickey Mouse'); assert(dictionary['person2'].Name === 'Goofy'); "); - } + } - [Fact] - public void CanSetIndexUsingStringKey() - { - var dictionary = new Dictionary(); - dictionary.Add("person1", new Person { Name = "Mickey Mouse" }); - dictionary.Add("person2", new Person { Name = "Goofy" }); + [Fact] + public void CanSetIndexUsingStringKey() + { + var dictionary = new Dictionary(); + dictionary.Add("person1", new Person { Name = "Mickey Mouse" }); + dictionary.Add("person2", new Person { Name = "Goofy" }); - _engine.SetValue("dictionary", dictionary); + _engine.SetValue("dictionary", dictionary); - RunTest(@" + RunTest(@" dictionary['person2'].Name = 'Donald Duck'; assert(dictionary['person2'].Name === 'Donald Duck'); "); - Assert.Equal("Donald Duck", dictionary["person2"].Name); - } + Assert.Equal("Donald Duck", dictionary["person2"].Name); + } - [Fact] - public void CanGetIndexUsingIntegerKey() - { - var dictionary = new Dictionary(); - dictionary.Add(1, "Mickey Mouse"); - dictionary.Add(2, "Goofy"); + [Fact] + public void CanGetIndexUsingIntegerKey() + { + var dictionary = new Dictionary(); + dictionary.Add(1, "Mickey Mouse"); + dictionary.Add(2, "Goofy"); - _engine.SetValue("dictionary", dictionary); + _engine.SetValue("dictionary", dictionary); - RunTest(@" + RunTest(@" assert(dictionary[1] === 'Mickey Mouse'); assert(dictionary[2] === 'Goofy'); "); - } + } - [Fact] - public void CanSetIndexUsingIntegerKey() - { - var dictionary = new Dictionary(); - dictionary.Add(1, "Mickey Mouse"); - dictionary.Add(2, "Goofy"); + [Fact] + public void CanSetIndexUsingIntegerKey() + { + var dictionary = new Dictionary(); + dictionary.Add(1, "Mickey Mouse"); + dictionary.Add(2, "Goofy"); - _engine.SetValue("dictionary", dictionary); + _engine.SetValue("dictionary", dictionary); - RunTest(@" + RunTest(@" dictionary[2] = 'Donald Duck'; assert(dictionary[2] === 'Donald Duck'); "); - Assert.Equal("Mickey Mouse", dictionary[1]); - Assert.Equal("Donald Duck", dictionary[2]); - } + Assert.Equal("Mickey Mouse", dictionary[1]); + Assert.Equal("Donald Duck", dictionary[2]); + } - private class DoubleIndexedClass - { - public int this[int index] => index; + private class DoubleIndexedClass + { + public int this[int index] => index; - public string this[string index] => index; - } + public string this[string index] => index; + } - [Fact] - public void CanGetIndexUsingBothIntAndStringIndex() - { - var dictionary = new DoubleIndexedClass(); + [Fact] + public void CanGetIndexUsingBothIntAndStringIndex() + { + var dictionary = new DoubleIndexedClass(); - _engine.SetValue("dictionary", dictionary); + _engine.SetValue("dictionary", dictionary); - RunTest(@" + RunTest(@" assert(dictionary[1] === 1); assert(dictionary['test'] === 'test'); "); - } + } - [Fact] - public void CanUseGenericMethods() - { - var dictionary = new Dictionary(); - dictionary.Add(1, "Mickey Mouse"); + [Fact] + public void CanUseGenericMethods() + { + var dictionary = new Dictionary(); + dictionary.Add(1, "Mickey Mouse"); - _engine.SetValue("dictionary", dictionary); + _engine.SetValue("dictionary", dictionary); - RunTest(@" + RunTest(@" dictionary.Add(2, 'Goofy'); assert(dictionary[2] === 'Goofy'); "); - Assert.Equal("Mickey Mouse", dictionary[1]); - Assert.Equal("Goofy", dictionary[2]); - } + Assert.Equal("Mickey Mouse", dictionary[1]); + Assert.Equal("Goofy", dictionary[2]); + } - [Fact] - public void CanUseMultiGenericTypes() - { - RunTest(@" + [Fact] + public void CanUseMultiGenericTypes() + { + RunTest(@" var type = System.Collections.Generic.Dictionary(System.Int32, System.String); var dictionary = new type(); dictionary.Add(1, 'Mickey Mouse'); dictionary.Add(2, 'Goofy'); assert(dictionary[2] === 'Goofy'); "); - } + } - [Fact] - public void CanUseIndexOnCollection() - { - var collection = new System.Collections.ObjectModel.Collection(); - collection.Add("Mickey Mouse"); - collection.Add("Goofy"); + [Fact] + public void CanUseIndexOnCollection() + { + var collection = new System.Collections.ObjectModel.Collection(); + collection.Add("Mickey Mouse"); + collection.Add("Goofy"); - _engine.SetValue("dictionary", collection); + _engine.SetValue("dictionary", collection); - RunTest(@" + RunTest(@" dictionary[1] = 'Donald Duck'; assert(dictionary[1] === 'Donald Duck'); "); - Assert.Equal("Mickey Mouse", collection[0]); - Assert.Equal("Donald Duck", collection[1]); - } + Assert.Equal("Mickey Mouse", collection[0]); + Assert.Equal("Donald Duck", collection[1]); + } - [Fact] - public void CanUseIndexOnList() - { - var list = new List(2); - list.Add("Mickey Mouse"); - list.Add("Goofy"); + [Fact] + public void CanUseIndexOnList() + { + var list = new List(2); + list.Add("Mickey Mouse"); + list.Add("Goofy"); - _engine.SetValue("list", list); - _engine.Evaluate("list[1] = 'Donald Duck';"); + _engine.SetValue("list", list); + _engine.Evaluate("list[1] = 'Donald Duck';"); - Assert.Equal("Donald Duck", _engine.Evaluate("list[1]").AsString()); - Assert.Equal("Mickey Mouse", list[0]); - Assert.Equal("Donald Duck", list[1]); - } + Assert.Equal("Donald Duck", _engine.Evaluate("list[1]").AsString()); + Assert.Equal("Mickey Mouse", list[0]); + Assert.Equal("Donald Duck", list[1]); + } - [Fact] - public void ShouldForOfOnLists() - { - _engine.SetValue("list", new List { "a", "b" }); + [Fact] + public void ShouldForOfOnLists() + { + _engine.SetValue("list", new List { "a", "b" }); - var result = _engine.Evaluate("var l = ''; for (var x of list) l += x; return l;").AsString(); + var result = _engine.Evaluate("var l = ''; for (var x of list) l += x; return l;").AsString(); - Assert.Equal("ab", result); - } + Assert.Equal("ab", result); + } - [Fact] - public void ShouldForOfOnArrays() - { - _engine.SetValue("arr", new[] { "a", "b" }); + [Fact] + public void ShouldForOfOnArrays() + { + _engine.SetValue("arr", new[] { "a", "b" }); - var result = _engine.Evaluate("var l = ''; for (var x of arr) l += x; return l;").AsString(); + var result = _engine.Evaluate("var l = ''; for (var x of arr) l += x; return l;").AsString(); - Assert.Equal("ab", result); - } + Assert.Equal("ab", result); + } - [Fact] - public void ShouldForOfOnDictionaries() - { - _engine.SetValue("dict", new Dictionary { { "a", "1" }, { "b", "2" } }); + [Fact] + public void ShouldForOfOnDictionaries() + { + _engine.SetValue("dict", new Dictionary { { "a", "1" }, { "b", "2" } }); - var result = _engine.Evaluate("var l = ''; for (var x of dict) l += x; return l;").AsString(); + var result = _engine.Evaluate("var l = ''; for (var x of dict) l += x; return l;").AsString(); - Assert.Equal("a,1b,2", result); - } + Assert.Equal("a,1b,2", result); + } - [Fact] - public void ShouldForOfOnEnumerable() - { - _engine.SetValue("c", new Company("name")); + [Fact] + public void ShouldForOfOnEnumerable() + { + _engine.SetValue("c", new Company("name")); - var result = _engine.Evaluate("var l = ''; for (var x of c.getNameChars()) l += x + ','; return l;").AsString(); + var result = _engine.Evaluate("var l = ''; for (var x of c.getNameChars()) l += x + ','; return l;").AsString(); - Assert.Equal("n,a,m,e,", result); - } + Assert.Equal("n,a,m,e,", result); + } - [Fact] - public void ShouldThrowWhenForOfOnObject() - { - // normal objects are not iterable in javascript - var o = new { A = 1, B = 2 }; - _engine.SetValue("anonymous", o); + [Fact] + public void ShouldThrowWhenForOfOnObject() + { + // normal objects are not iterable in javascript + var o = new { A = 1, B = 2 }; + _engine.SetValue("anonymous", o); - var ex = Assert.Throws(() => _engine.Evaluate("for (var x of anonymous) {}")); - Assert.Equal("The value is not iterable", ex.Message); - } + var ex = Assert.Throws(() => _engine.Evaluate("for (var x of anonymous) {}")); + Assert.Equal("The value is not iterable", ex.Message); + } - [Fact] - public void CanAccessAnonymousObject() + [Fact] + public void CanAccessAnonymousObject() + { + var p = new { - var p = new - { - Name = "Mickey Mouse" - }; + Name = "Mickey Mouse" + }; - _engine.SetValue("p", p); + _engine.SetValue("p", p); - RunTest(@" + RunTest(@" assert(p.Name === 'Mickey Mouse'); "); - } + } - [Fact] - public void CanAccessAnonymousObjectProperties() + [Fact] + public void CanAccessAnonymousObjectProperties() + { + var p = new { - var p = new + Address = new { - Address = new - { - City = "Mouseton" - } - }; + City = "Mouseton" + } + }; - _engine.SetValue("p", p); + _engine.SetValue("p", p); - RunTest(@" + RunTest(@" assert(p.Address.City === 'Mouseton'); "); - } + } - [Fact] - public void PocosCanReturnJsValueDirectly() + [Fact] + public void PocosCanReturnJsValueDirectly() + { + var o = new { - var o = new - { - x = new JsNumber(1), - y = new JsString("string") - }; + x = new JsNumber(1), + y = new JsString("string") + }; - _engine.SetValue("o", o); + _engine.SetValue("o", o); - RunTest(@" + RunTest(@" assert(o.x === 1); assert(o.y === 'string'); "); - } + } - [Fact] - public void PocosCanReturnObjectInstanceDirectly() - { - var x = new JsObject(_engine); - x.Set("foo", new JsString("bar")); + [Fact] + public void PocosCanReturnObjectInstanceDirectly() + { + var x = new JsObject(_engine); + x.Set("foo", new JsString("bar")); - var o = new - { - x - }; + var o = new + { + x + }; - _engine.SetValue("o", o); + _engine.SetValue("o", o); - RunTest(@" + RunTest(@" assert(o.x.foo === 'bar'); "); - } + } - [Fact] - public void DateTimeIsConvertedToDate() + [Fact] + public void DateTimeIsConvertedToDate() + { + var o = new { - var o = new - { - z = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) - }; + z = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) + }; - _engine.SetValue("o", o); + _engine.SetValue("o", o); - RunTest(@" + RunTest(@" assert(o.z.valueOf() === 0); "); - } + } - [Fact] - public void DateTimeOffsetIsConvertedToDate() + [Fact] + public void DateTimeOffsetIsConvertedToDate() + { + var o = new { - var o = new - { - z = new DateTimeOffset(1970, 1, 1, 0, 0, 0, new TimeSpan()) - }; + z = new DateTimeOffset(1970, 1, 1, 0, 0, 0, new TimeSpan()) + }; - _engine.SetValue("o", o); + _engine.SetValue("o", o); - RunTest(@" + RunTest(@" assert(o.z.valueOf() === 0); "); - } + } - [Fact] - public void EcmaValuesAreAutomaticallyConvertedWhenSetInPoco() + [Fact] + public void EcmaValuesAreAutomaticallyConvertedWhenSetInPoco() + { + var p = new Person { - var p = new Person - { - Name = "foo" - }; + Name = "foo" + }; - _engine.SetValue("p", p); + _engine.SetValue("p", p); - RunTest(@" + RunTest(@" assert(p.Name === 'foo'); assert(p.Age === 0); p.Name = 'bar'; p.Age = 10; "); - Assert.Equal("bar", p.Name); - Assert.Equal(10, p.Age); - } + Assert.Equal("bar", p.Name); + Assert.Equal(10, p.Age); + } - [Fact] - public void EcmaValuesAreAutomaticallyConvertedToBestMatchWhenSetInPoco() + [Fact] + public void EcmaValuesAreAutomaticallyConvertedToBestMatchWhenSetInPoco() + { + var p = new Person { - var p = new Person - { - Name = "foo" - }; + Name = "foo" + }; - _engine.SetValue("p", p); + _engine.SetValue("p", p); - RunTest(@" + RunTest(@" p.Name = 10; p.Age = '20'; "); - Assert.Equal("10", p.Name); - Assert.Equal(20, p.Age); - } + Assert.Equal("10", p.Name); + Assert.Equal(20, p.Age); + } - [Fact] - public void ShouldCallInstanceMethodWithoutArgument() - { - _engine.SetValue("a", new A()); + [Fact] + public void ShouldCallInstanceMethodWithoutArgument() + { + _engine.SetValue("a", new A()); - RunTest(@" + RunTest(@" assert(a.Call1() === 0); "); - } + } - [Fact] - public void ShouldCallInstanceMethodOverloadArgument() - { - _engine.SetValue("a", new A()); + [Fact] + public void ShouldCallInstanceMethodOverloadArgument() + { + _engine.SetValue("a", new A()); - RunTest(@" + RunTest(@" assert(a.Call1(1) === 1); "); - } + } - [Fact] - public void ShouldCallInstanceMethodWithString() - { - var p = new Person(); - _engine.SetValue("a", new A()); - _engine.SetValue("p", p); + [Fact] + public void ShouldCallInstanceMethodWithString() + { + var p = new Person(); + _engine.SetValue("a", new A()); + _engine.SetValue("p", p); - RunTest(@" + RunTest(@" p.Name = a.Call2('foo'); assert(p.Name === 'foo'); "); - Assert.Equal("foo", p.Name); - } + Assert.Equal("foo", p.Name); + } - [Fact] - public void CanUseTrim() - { - var p = new Person { Name = "Mickey Mouse " }; - _engine.SetValue("p", p); + [Fact] + public void CanUseTrim() + { + var p = new Person { Name = "Mickey Mouse " }; + _engine.SetValue("p", p); - RunTest(@" + RunTest(@" assert(p.Name === 'Mickey Mouse '); p.Name = p.Name.trim(); assert(p.Name === 'Mickey Mouse'); "); - Assert.Equal("Mickey Mouse", p.Name); - } + Assert.Equal("Mickey Mouse", p.Name); + } - [Fact] - public void CanUseMathFloor() - { - var p = new Person(); - _engine.SetValue("p", p); + [Fact] + public void CanUseMathFloor() + { + var p = new Person(); + _engine.SetValue("p", p); - RunTest(@" + RunTest(@" p.Age = Math.floor(1.6);p assert(p.Age === 1); "); - Assert.Equal(1, p.Age); - } + Assert.Equal(1, p.Age); + } - [Fact] - public void CanUseDelegateAsFunction() - { - var even = new Func(x => x % 2 == 0); - _engine.SetValue("even", even); + [Fact] + public void CanUseDelegateAsFunction() + { + var even = new Func(x => x % 2 == 0); + _engine.SetValue("even", even); - RunTest(@" + RunTest(@" assert(even(2) === true); "); - } - - private struct TestStruct - { - public int Value; + } - public TestStruct(int value) - { - Value = value; - } - } + private struct TestStruct + { + public int Value; - private class TestClass + public TestStruct(int value) { - public string String { get; set; } - public int Int { get; set; } - public int? NullableInt { get; set; } - public DateTime? NullableDate { get; set; } - public bool? NullableBool { get; set; } - public bool Bool { get; set; } - public TestEnumInt32? NullableEnum { get; set; } - public TestStruct? NullableStruct { get; set; } + Value = value; + } + } - public void SetBool(bool value) - { - Bool = value; - } + private class TestClass + { + public string String { get; set; } + public int Int { get; set; } + public int? NullableInt { get; set; } + public DateTime? NullableDate { get; set; } + public bool? NullableBool { get; set; } + public bool Bool { get; set; } + public TestEnumInt32? NullableEnum { get; set; } + public TestStruct? NullableStruct { get; set; } - public void SetInt(int value) - { - Int = value; - } + public void SetBool(bool value) + { + Bool = value; + } - public void SetString(string value) - { - String = value; - } + public void SetInt(int value) + { + Int = value; } - [Fact] - public void CanSetNullablePropertiesOnPocos() + public void SetString(string value) { - var instance = new TestClass(); - _engine.SetValue("instance", instance); - _engine.SetValue("TestStruct", typeof(TestStruct)); + String = value; + } + } - RunTest(@" + [Fact] + public void CanSetNullablePropertiesOnPocos() + { + var instance = new TestClass(); + _engine.SetValue("instance", instance); + _engine.SetValue("TestStruct", typeof(TestStruct)); + + RunTest(@" instance.NullableInt = 2; instance.NullableDate = new Date(); instance.NullableBool = true; @@ -816,446 +816,446 @@ public void CanSetNullablePropertiesOnPocos() assert(instance.NullableEnum===1); assert(instance.NullableStruct.Value===5); "); - } + } - private class ReadOnlyList : IReadOnlyList + private class ReadOnlyList : IReadOnlyList + { + private readonly Person[] _data; + + public ReadOnlyList(params Person[] data) { - private readonly Person[] _data; + _data = data; + } - public ReadOnlyList(params Person[] data) - { - _data = data; - } + public IEnumerator GetEnumerator() + { + return ((IEnumerable) _data).GetEnumerator(); + } - public IEnumerator GetEnumerator() - { - return ((IEnumerable) _data).GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() + { + return _data.GetEnumerator(); + } - IEnumerator IEnumerable.GetEnumerator() - { - return _data.GetEnumerator(); - } + public int Count => _data.Length; - public int Count => _data.Length; + public Person this[int index] => _data[index]; + } - public Person this[int index] => _data[index]; - } + [Fact] + public void CanAddArrayPrototypeForArrayLikeClrObjects() + { + var e = new Engine(cfg => cfg + .AllowClr(typeof(Person).Assembly) + ); - [Fact] - public void CanAddArrayPrototypeForArrayLikeClrObjects() + var person = new Person { - var e = new Engine(cfg => cfg - .AllowClr(typeof(Person).Assembly) - ); - - var person = new Person - { - Age = 12, - Name = "John" - }; + Age = 12, + Name = "John" + }; - dynamic obj = new - { - values = new ReadOnlyList(person) - }; + dynamic obj = new + { + values = new ReadOnlyList(person) + }; - e.SetValue("o", obj); + e.SetValue("o", obj); - var name = e.Evaluate("o.values.filter(x => x.age == 12)[0].name").ToString(); - Assert.Equal("John", name); - } + var name = e.Evaluate("o.values.filter(x => x.age == 12)[0].name").ToString(); + Assert.Equal("John", name); + } - [Fact] - public void CanSetIsConcatSpreadableForArrays() - { - var engine = new Engine(); + [Fact] + public void CanSetIsConcatSpreadableForArrays() + { + var engine = new Engine(); - engine - .SetValue("list1", new List { "A", "B", "C" }) - .SetValue("list2", new List { "D", "E", "F" }) - .Execute("var array1 = ['A', 'B', 'C'];") - .Execute("var array2 = ['D', 'E', 'F'];"); + engine + .SetValue("list1", new List { "A", "B", "C" }) + .SetValue("list2", new List { "D", "E", "F" }) + .Execute("var array1 = ['A', 'B', 'C'];") + .Execute("var array2 = ['D', 'E', 'F'];"); - Assert.True(engine.Evaluate("list1[Symbol.isConcatSpreadable] = true; list1[Symbol.isConcatSpreadable];").AsBoolean()); - Assert.True(engine.Evaluate("list2[Symbol.isConcatSpreadable] = true; list2[Symbol.isConcatSpreadable];").AsBoolean()); + Assert.True(engine.Evaluate("list1[Symbol.isConcatSpreadable] = true; list1[Symbol.isConcatSpreadable];").AsBoolean()); + Assert.True(engine.Evaluate("list2[Symbol.isConcatSpreadable] = true; list2[Symbol.isConcatSpreadable];").AsBoolean()); - Assert.Equal("[\"A\",\"B\",\"C\"]", engine.Evaluate("JSON.stringify(array1);")); - Assert.Equal("[\"D\",\"E\",\"F\"]", engine.Evaluate("JSON.stringify(array2);")); - Assert.Equal("[\"A\",\"B\",\"C\"]", engine.Evaluate("JSON.stringify(list1);")); - Assert.Equal("[\"D\",\"E\",\"F\"]", engine.Evaluate("JSON.stringify(list2);")); + Assert.Equal("[\"A\",\"B\",\"C\"]", engine.Evaluate("JSON.stringify(array1);")); + Assert.Equal("[\"D\",\"E\",\"F\"]", engine.Evaluate("JSON.stringify(array2);")); + Assert.Equal("[\"A\",\"B\",\"C\"]", engine.Evaluate("JSON.stringify(list1);")); + Assert.Equal("[\"D\",\"E\",\"F\"]", engine.Evaluate("JSON.stringify(list2);")); - const string Concatenated = "[\"A\",\"B\",\"C\",\"D\",\"E\",\"F\"]"; - Assert.Equal(Concatenated, engine.Evaluate("JSON.stringify(array1.concat(array2));")); - Assert.Equal(Concatenated, engine.Evaluate("JSON.stringify(array1.concat(list2));")); - Assert.Equal(Concatenated, engine.Evaluate("JSON.stringify(list1.concat(array2));")); - Assert.Equal(Concatenated, engine.Evaluate("JSON.stringify(list1.concat(list2));")); + const string Concatenated = "[\"A\",\"B\",\"C\",\"D\",\"E\",\"F\"]"; + Assert.Equal(Concatenated, engine.Evaluate("JSON.stringify(array1.concat(array2));")); + Assert.Equal(Concatenated, engine.Evaluate("JSON.stringify(array1.concat(list2));")); + Assert.Equal(Concatenated, engine.Evaluate("JSON.stringify(list1.concat(array2));")); + Assert.Equal(Concatenated, engine.Evaluate("JSON.stringify(list1.concat(list2));")); - Assert.False(engine.Evaluate("list1[Symbol.isConcatSpreadable] = false; list1[Symbol.isConcatSpreadable];").AsBoolean()); - Assert.False(engine.Evaluate("list2[Symbol.isConcatSpreadable] = false; list2[Symbol.isConcatSpreadable];").AsBoolean()); + Assert.False(engine.Evaluate("list1[Symbol.isConcatSpreadable] = false; list1[Symbol.isConcatSpreadable];").AsBoolean()); + Assert.False(engine.Evaluate("list2[Symbol.isConcatSpreadable] = false; list2[Symbol.isConcatSpreadable];").AsBoolean()); - Assert.Equal("[[\"A\",\"B\",\"C\"]]", engine.Evaluate("JSON.stringify([].concat(list1));")); - Assert.Equal("[[\"A\",\"B\",\"C\"],[\"D\",\"E\",\"F\"]]", engine.Evaluate("JSON.stringify(list1.concat(list2));")); - } + Assert.Equal("[[\"A\",\"B\",\"C\"]]", engine.Evaluate("JSON.stringify([].concat(list1));")); + Assert.Equal("[[\"A\",\"B\",\"C\"],[\"D\",\"E\",\"F\"]]", engine.Evaluate("JSON.stringify(list1.concat(list2));")); + } - [Fact] - public void ShouldConvertArrayToArrayInstance() - { - var result = _engine - .SetValue("values", new[] { 1, 2, 3, 4, 5, 6 }) - .Evaluate("values.filter(function(x){ return x % 2 == 0; })"); + [Fact] + public void ShouldConvertArrayToArrayInstance() + { + var result = _engine + .SetValue("values", new[] { 1, 2, 3, 4, 5, 6 }) + .Evaluate("values.filter(function(x){ return x % 2 == 0; })"); - var parts = result.ToObject(); + var parts = result.ToObject(); - Assert.True(parts.GetType().IsArray); - Assert.Equal(3, ((object[]) parts).Length); - Assert.Equal(2d, ((object[]) parts)[0]); - Assert.Equal(4d, ((object[]) parts)[1]); - Assert.Equal(6d, ((object[]) parts)[2]); - } + Assert.True(parts.GetType().IsArray); + Assert.Equal(3, ((object[]) parts).Length); + Assert.Equal(2d, ((object[]) parts)[0]); + Assert.Equal(4d, ((object[]) parts)[1]); + Assert.Equal(6d, ((object[]) parts)[2]); + } - [Fact] - public void ShouldConvertListsToArrayInstance() - { - var result = _engine - .SetValue("values", new List { 1, 2, 3, 4, 5, 6 }) - .Evaluate("new Array(values).filter(function(x){ return x % 2 == 0; })"); + [Fact] + public void ShouldConvertListsToArrayInstance() + { + var result = _engine + .SetValue("values", new List { 1, 2, 3, 4, 5, 6 }) + .Evaluate("new Array(values).filter(function(x){ return x % 2 == 0; })"); - var parts = result.ToObject(); + var parts = result.ToObject(); - Assert.True(parts.GetType().IsArray); - Assert.Equal(3, ((object[]) parts).Length); - Assert.Equal(2d, ((object[]) parts)[0]); - Assert.Equal(4d, ((object[]) parts)[1]); - Assert.Equal(6d, ((object[]) parts)[2]); - } + Assert.True(parts.GetType().IsArray); + Assert.Equal(3, ((object[]) parts).Length); + Assert.Equal(2d, ((object[]) parts)[0]); + Assert.Equal(4d, ((object[]) parts)[1]); + Assert.Equal(6d, ((object[]) parts)[2]); + } - [Fact] - public void ShouldConvertArrayInstanceToArray() - { - var parts = _engine.Evaluate("'foo@bar.com'.split('@');").ToObject(); + [Fact] + public void ShouldConvertArrayInstanceToArray() + { + var parts = _engine.Evaluate("'foo@bar.com'.split('@');").ToObject(); - Assert.True(parts.GetType().IsArray); - Assert.Equal(2, ((object[]) parts).Length); - Assert.Equal("foo", ((object[]) parts)[0]); - Assert.Equal("bar.com", ((object[]) parts)[1]); - } + Assert.True(parts.GetType().IsArray); + Assert.Equal(2, ((object[]) parts).Length); + Assert.Equal("foo", ((object[]) parts)[0]); + Assert.Equal("bar.com", ((object[]) parts)[1]); + } - [Fact] - public void ShouldLoopWithNativeEnumerator() + [Fact] + public void ShouldLoopWithNativeEnumerator() + { + JsValue adder(JsValue argValue) { - JsValue adder(JsValue argValue) + var args = argValue.AsArray(); + double sum = 0; + foreach (var item in args) { - var args = argValue.AsArray(); - double sum = 0; - foreach (var item in args) + if (item.IsNumber()) { - if (item.IsNumber()) - { - sum += item.AsNumber(); - } + sum += item.AsNumber(); } - - return sum; } - var result = _engine.SetValue("getSum", new Func(adder)) - .Evaluate("getSum([1,2,3]);"); - - Assert.True(result == 6); + return sum; } - [Fact] - public void ShouldConvertBooleanInstanceToBool() - { - var value = _engine.Evaluate("new Boolean(true)").ToObject(); + var result = _engine.SetValue("getSum", new Func(adder)) + .Evaluate("getSum([1,2,3]);"); - Assert.Equal(typeof(bool), value.GetType()); - Assert.Equal(true, value); - } + Assert.True(result == 6); + } - [Fact] - public void ShouldAllowBooleanCoercion() - { - var engine = new Engine(options => { options.Interop.ValueCoercion = ValueCoercionType.Boolean; }); + [Fact] + public void ShouldConvertBooleanInstanceToBool() + { + var value = _engine.Evaluate("new Boolean(true)").ToObject(); - engine.SetValue("o", new TestClass()); - Assert.True(engine.Evaluate("o.Bool = 1; return o.Bool;").AsBoolean()); - Assert.True(engine.Evaluate("o.Bool = 'dog'; return o.Bool;").AsBoolean()); - Assert.True(engine.Evaluate("o.Bool = {}; return o.Bool;").AsBoolean()); - Assert.False(engine.Evaluate("o.Bool = 0; return o.Bool;").AsBoolean()); - Assert.False(engine.Evaluate("o.Bool = ''; return o.Bool;").AsBoolean()); - Assert.False(engine.Evaluate("o.Bool = null; return o.Bool;").AsBoolean()); - Assert.False(engine.Evaluate("o.Bool = undefined; return o.Bool;").AsBoolean()); + Assert.Equal(typeof(bool), value.GetType()); + Assert.Equal(true, value); + } - engine.Evaluate("class MyClass { valueOf() { return 42; } }"); - Assert.Equal(true, engine.Evaluate("let obj = new MyClass(); o.Bool = obj; return o.Bool;").AsBoolean()); + [Fact] + public void ShouldAllowBooleanCoercion() + { + var engine = new Engine(options => { options.Interop.ValueCoercion = ValueCoercionType.Boolean; }); + + engine.SetValue("o", new TestClass()); + Assert.True(engine.Evaluate("o.Bool = 1; return o.Bool;").AsBoolean()); + Assert.True(engine.Evaluate("o.Bool = 'dog'; return o.Bool;").AsBoolean()); + Assert.True(engine.Evaluate("o.Bool = {}; return o.Bool;").AsBoolean()); + Assert.False(engine.Evaluate("o.Bool = 0; return o.Bool;").AsBoolean()); + Assert.False(engine.Evaluate("o.Bool = ''; return o.Bool;").AsBoolean()); + Assert.False(engine.Evaluate("o.Bool = null; return o.Bool;").AsBoolean()); + Assert.False(engine.Evaluate("o.Bool = undefined; return o.Bool;").AsBoolean()); + + engine.Evaluate("class MyClass { valueOf() { return 42; } }"); + Assert.Equal(true, engine.Evaluate("let obj = new MyClass(); o.Bool = obj; return o.Bool;").AsBoolean()); + + engine.SetValue("func3", new Action((param1, param2, param3) => + { + Assert.True(param1); + Assert.True(param2); + Assert.True(param3); + })); + engine.Evaluate("func3(true, obj, [ 1, 2, 3])"); + + Assert.Equal(true, engine.Evaluate("o.SetBool(42); return o.Bool;").AsBoolean()); + Assert.Equal(true, engine.Evaluate("o.SetBool(obj); return o.Bool;").AsBoolean()); + Assert.Equal(true, engine.Evaluate("o.SetBool([ 1, 2, 3].length); return o.Bool;").AsBoolean()); + } - engine.SetValue("func3", new Action((param1, param2, param3) => - { - Assert.True(param1); - Assert.True(param2); - Assert.True(param3); - })); - engine.Evaluate("func3(true, obj, [ 1, 2, 3])"); - - Assert.Equal(true, engine.Evaluate("o.SetBool(42); return o.Bool;").AsBoolean()); - Assert.Equal(true, engine.Evaluate("o.SetBool(obj); return o.Bool;").AsBoolean()); - Assert.Equal(true, engine.Evaluate("o.SetBool([ 1, 2, 3].length); return o.Bool;").AsBoolean()); - } + [Fact] + public void ShouldAllowNumberCoercion() + { + var engine = new Engine(options => { options.Interop.ValueCoercion = ValueCoercionType.Number; }); + + engine.SetValue("o", new TestClass()); + Assert.Equal(1, engine.Evaluate("o.Int = true; return o.Int;").AsNumber()); + Assert.Equal(0, engine.Evaluate("o.Int = false; return o.Int;").AsNumber()); + + engine.Evaluate("class MyClass { valueOf() { return 42; } }"); + Assert.Equal(42, engine.Evaluate("let obj = new MyClass(); o.Int = obj; return o.Int;").AsNumber()); - [Fact] - public void ShouldAllowNumberCoercion() + // but null and undefined should be injected as nulls to nullable objects + Assert.True(engine.Evaluate("o.NullableInt = null; return o.NullableInt;").IsNull()); + Assert.True(engine.Evaluate("o.NullableInt = undefined; return o.NullableInt;").IsNull()); + + engine.SetValue("func3", new Action((param1, param2, param3) => { - var engine = new Engine(options => { options.Interop.ValueCoercion = ValueCoercionType.Number; }); + Assert.Equal(1, param1); + Assert.Equal(42, param2); + Assert.Equal(3, param3); + })); + engine.Evaluate("func3(true, obj, [ 1, 2, 3].length)"); - engine.SetValue("o", new TestClass()); - Assert.Equal(1, engine.Evaluate("o.Int = true; return o.Int;").AsNumber()); - Assert.Equal(0, engine.Evaluate("o.Int = false; return o.Int;").AsNumber()); + Assert.Equal(1, engine.Evaluate("o.SetInt(true); return o.Int;").AsNumber()); + Assert.Equal(42, engine.Evaluate("o.SetInt(obj); return o.Int;").AsNumber()); + Assert.Equal(3, engine.Evaluate("o.SetInt([ 1, 2, 3].length); return o.Int;").AsNumber()); + } - engine.Evaluate("class MyClass { valueOf() { return 42; } }"); - Assert.Equal(42, engine.Evaluate("let obj = new MyClass(); o.Int = obj; return o.Int;").AsNumber()); + [Fact] + public void ShouldAllowStringCoercion() + { + var engine = new Engine(options => { options.Interop.ValueCoercion = ValueCoercionType.String; }); - // but null and undefined should be injected as nulls to nullable objects - Assert.True(engine.Evaluate("o.NullableInt = null; return o.NullableInt;").IsNull()); - Assert.True(engine.Evaluate("o.NullableInt = undefined; return o.NullableInt;").IsNull()); + // basic premise, booleans in JS are lower-case, so should the the toString under interop + Assert.Equal("true", engine.Evaluate("'' + true").AsString()); - engine.SetValue("func3", new Action((param1, param2, param3) => - { - Assert.Equal(1, param1); - Assert.Equal(42, param2); - Assert.Equal(3, param3); - })); - engine.Evaluate("func3(true, obj, [ 1, 2, 3].length)"); - - Assert.Equal(1, engine.Evaluate("o.SetInt(true); return o.Int;").AsNumber()); - Assert.Equal(42, engine.Evaluate("o.SetInt(obj); return o.Int;").AsNumber()); - Assert.Equal(3, engine.Evaluate("o.SetInt([ 1, 2, 3].length); return o.Int;").AsNumber()); - } + engine.SetValue("o", new TestClass()); + Assert.Equal("false", engine.Evaluate("'' + o.Bool").AsString()); - [Fact] - public void ShouldAllowStringCoercion() - { - var engine = new Engine(options => { options.Interop.ValueCoercion = ValueCoercionType.String; }); + Assert.Equal("true", engine.Evaluate("o.Bool = true; o.String = o.Bool; return o.String;").AsString()); - // basic premise, booleans in JS are lower-case, so should the the toString under interop - Assert.Equal("true", engine.Evaluate("'' + true").AsString()); + Assert.Equal("true", engine.Evaluate("o.String = true; return o.String;").AsString()); - engine.SetValue("o", new TestClass()); - Assert.Equal("false", engine.Evaluate("'' + o.Bool").AsString()); + engine.SetValue("func1", new Func(() => true)); + Assert.Equal("true", engine.Evaluate("'' + func1()").AsString()); - Assert.Equal("true", engine.Evaluate("o.Bool = true; o.String = o.Bool; return o.String;").AsString()); + engine.SetValue("func2", new Func(() => JsBoolean.True)); + Assert.Equal("true", engine.Evaluate("'' + func2()").AsString()); - Assert.Equal("true", engine.Evaluate("o.String = true; return o.String;").AsString()); + // but null and undefined should be injected as nulls to c# objects + Assert.True(engine.Evaluate("o.String = null; return o.String;").IsNull()); + Assert.True(engine.Evaluate("o.String = undefined; return o.String;").IsNull()); - engine.SetValue("func1", new Func(() => true)); - Assert.Equal("true", engine.Evaluate("'' + func1()").AsString()); + Assert.Equal("1,2,3", engine.Evaluate("o.String = [ 1, 2, 3 ]; return o.String;").AsString()); - engine.SetValue("func2", new Func(() => JsBoolean.True)); - Assert.Equal("true", engine.Evaluate("'' + func2()").AsString()); + engine.Evaluate("class MyClass { toString() { return 'hello world'; } }"); + Assert.Equal("hello world", engine.Evaluate("let obj = new MyClass(); o.String = obj; return o.String;").AsString()); + + engine.SetValue("func3", new Action((param1, param2, param3) => + { + Assert.Equal("true", param1); + Assert.Equal("hello world", param2); + Assert.Equal("1,2,3", param3); + })); + engine.Evaluate("func3(true, obj, [ 1, 2, 3])"); - // but null and undefined should be injected as nulls to c# objects - Assert.True(engine.Evaluate("o.String = null; return o.String;").IsNull()); - Assert.True(engine.Evaluate("o.String = undefined; return o.String;").IsNull()); + Assert.Equal("true", engine.Evaluate("o.SetString(true); return o.String;").AsString()); + Assert.Equal("hello world", engine.Evaluate("o.SetString(obj); return o.String;").AsString()); + Assert.Equal("1,2,3", engine.Evaluate("o.SetString([ 1, 2, 3]); return o.String;").AsString()); + } - Assert.Equal("1,2,3", engine.Evaluate("o.String = [ 1, 2, 3 ]; return o.String;").AsString()); + [Fact] + public void ShouldConvertDateInstanceToDateTime() + { + var result = _engine.Evaluate("new Date(0)"); + var value = result.ToObject() is DateTime ? (DateTime) result.ToObject() : default; - engine.Evaluate("class MyClass { toString() { return 'hello world'; } }"); - Assert.Equal("hello world", engine.Evaluate("let obj = new MyClass(); o.String = obj; return o.String;").AsString()); + Assert.Equal(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), value); + Assert.Equal(DateTimeKind.Utc, value.Kind); + } - engine.SetValue("func3", new Action((param1, param2, param3) => - { - Assert.Equal("true", param1); - Assert.Equal("hello world", param2); - Assert.Equal("1,2,3", param3); - })); - engine.Evaluate("func3(true, obj, [ 1, 2, 3])"); - - Assert.Equal("true", engine.Evaluate("o.SetString(true); return o.String;").AsString()); - Assert.Equal("hello world", engine.Evaluate("o.SetString(obj); return o.String;").AsString()); - Assert.Equal("1,2,3", engine.Evaluate("o.SetString([ 1, 2, 3]); return o.String;").AsString()); + [Fact] + public void ShouldConvertDateInstanceToLocalDateTime() + { + TimeZoneInfo timeZone; + try + { + timeZone = TimeZoneInfo.FindSystemTimeZoneById("Europe/Helsinki"); } - - [Fact] - public void ShouldConvertDateInstanceToDateTime() + catch (TimeZoneNotFoundException) { - var result = _engine.Evaluate("new Date(0)"); - var value = result.ToObject() is DateTime ? (DateTime) result.ToObject() : default; - - Assert.Equal(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), value); - Assert.Equal(DateTimeKind.Utc, value.Kind); + timeZone = TimeZoneInfo.FindSystemTimeZoneById("FLE Standard Time"); } - [Fact] - public void ShouldConvertDateInstanceToLocalDateTime() + var engine = new Engine(options => { - TimeZoneInfo timeZone; - try - { - timeZone = TimeZoneInfo.FindSystemTimeZoneById("Europe/Helsinki"); - } - catch (TimeZoneNotFoundException) - { - timeZone = TimeZoneInfo.FindSystemTimeZoneById("FLE Standard Time"); - } + options.TimeZone = timeZone; + options.Interop.DateTimeKind = DateTimeKind.Local; + }); - var engine = new Engine(options => - { - options.TimeZone = timeZone; - options.Interop.DateTimeKind = DateTimeKind.Local; - }); + var result = engine.Evaluate("new Date(0)"); + var value = result.ToObject() is DateTime ? (DateTime) result.ToObject() : default; - var result = engine.Evaluate("new Date(0)"); - var value = result.ToObject() is DateTime ? (DateTime) result.ToObject() : default; - - Assert.Equal(new DateTime(1970, 1, 1, 2, 0, 0, DateTimeKind.Local), value); - Assert.Equal(DateTimeKind.Local, value.Kind); - } + Assert.Equal(new DateTime(1970, 1, 1, 2, 0, 0, DateTimeKind.Local), value); + Assert.Equal(DateTimeKind.Local, value.Kind); + } - [Fact] - public void ShouldConvertNumberInstanceToDouble() - { - var result = _engine.Evaluate("new Number(10)"); - var value = result.ToObject(); + [Fact] + public void ShouldConvertNumberInstanceToDouble() + { + var result = _engine.Evaluate("new Number(10)"); + var value = result.ToObject(); - Assert.Equal(typeof(double), value.GetType()); - Assert.Equal(10d, value); - } + Assert.Equal(typeof(double), value.GetType()); + Assert.Equal(10d, value); + } - [Fact] - public void ShouldConvertStringInstanceToString() - { - var value = _engine.Evaluate("new String('foo')").ToObject(); + [Fact] + public void ShouldConvertStringInstanceToString() + { + var value = _engine.Evaluate("new String('foo')").ToObject(); - Assert.Equal(typeof(string), value.GetType()); - Assert.Equal("foo", value); - } + Assert.Equal(typeof(string), value.GetType()); + Assert.Equal("foo", value); + } - [Fact] - public void ShouldNotTryToConvertCompatibleTypes() - { - _engine.SetValue("a", new A()); + [Fact] + public void ShouldNotTryToConvertCompatibleTypes() + { + _engine.SetValue("a", new A()); - RunTest(@" + RunTest(@" assert(a.Call3('foo') === 'foo'); assert(a.Call3(1) === '1'); "); - } + } - [Fact] - public void ShouldNotTryToConvertDerivedTypes() - { - _engine.SetValue("a", new A()); - _engine.SetValue("p", new Person { Name = "Mickey" }); + [Fact] + public void ShouldNotTryToConvertDerivedTypes() + { + _engine.SetValue("a", new A()); + _engine.SetValue("p", new Person { Name = "Mickey" }); - RunTest(@" + RunTest(@" assert(a.Call4(p) === 'Mickey'); "); - } + } - [Fact] - public void ShouldExecuteFunctionCallBackAsDelegate() - { - _engine.SetValue("a", new A()); + [Fact] + public void ShouldExecuteFunctionCallBackAsDelegate() + { + _engine.SetValue("a", new A()); - RunTest(@" + RunTest(@" assert(a.Call5(function(a,b){ return a+b }) === '1foo'); "); - } + } - [Fact] - public void ShouldExecuteFunctionCallBackAsFuncAndThisCanBeAssigned() - { - _engine.SetValue("a", new A()); + [Fact] + public void ShouldExecuteFunctionCallBackAsFuncAndThisCanBeAssigned() + { + _engine.SetValue("a", new A()); - RunTest(@" + RunTest(@" assert(a.Call6(function(a,b){ return this+a+b }) === 'bar1foo'); "); - } + } - [Fact] - public void ShouldExecuteFunctionCallBackAsPredicate() - { - _engine.SetValue("a", new A()); + [Fact] + public void ShouldExecuteFunctionCallBackAsPredicate() + { + _engine.SetValue("a", new A()); - // Func<> - RunTest(@" + // Func<> + RunTest(@" assert(a.Call8(function(){ return 'foo'; }) === 'foo'); "); - } + } - [Fact] - public void ShouldExecuteFunctionWithParameterCallBackAsPredicate() - { - _engine.SetValue("a", new A()); + [Fact] + public void ShouldExecuteFunctionWithParameterCallBackAsPredicate() + { + _engine.SetValue("a", new A()); - // Func<,> - RunTest(@" + // Func<,> + RunTest(@" assert(a.Call7('foo', function(a){ return a === 'foo'; }) === true); "); - } + } - [Fact] - public void ShouldExecuteActionCallBackAsPredicate() - { - _engine.SetValue("a", new A()); + [Fact] + public void ShouldExecuteActionCallBackAsPredicate() + { + _engine.SetValue("a", new A()); - // Action - RunTest(@" + // Action + RunTest(@" var value; a.Call9(function(){ value = 'foo'; }); assert(value === 'foo'); "); - } + } - [Fact] - public void ShouldExecuteActionWithParameterCallBackAsPredicate() - { - _engine.SetValue("a", new A()); + [Fact] + public void ShouldExecuteActionWithParameterCallBackAsPredicate() + { + _engine.SetValue("a", new A()); - // Action<> - RunTest(@" + // Action<> + RunTest(@" var value; a.Call10('foo', function(b){ value = b; }); assert(value === 'foo'); "); - } + } - [Fact] - public void ShouldExecuteActionWithMultipleParametersCallBackAsPredicate() - { - _engine.SetValue("a", new A()); + [Fact] + public void ShouldExecuteActionWithMultipleParametersCallBackAsPredicate() + { + _engine.SetValue("a", new A()); - // Action<,> - RunTest(@" + // Action<,> + RunTest(@" var value; a.Call11('foo', 'bar', function(a,b){ value = a + b; }); assert(value === 'foobar'); "); - } + } - [Fact] - public void ShouldExecuteFunc() - { - _engine.SetValue("a", new A()); + [Fact] + public void ShouldExecuteFunc() + { + _engine.SetValue("a", new A()); - // Func - RunTest(@" + // Func + RunTest(@" var result = a.Call12(42, function(a){ return a + a; }); assert(result === 84); "); - } + } - [Fact] - public void ShouldExecuteActionCallbackOnEventChanged() - { - var collection = new System.Collections.ObjectModel.ObservableCollection(); - Assert.True(collection.Count == 0); + [Fact] + public void ShouldExecuteActionCallbackOnEventChanged() + { + var collection = new System.Collections.ObjectModel.ObservableCollection(); + Assert.True(collection.Count == 0); - _engine.SetValue("collection", collection); + _engine.SetValue("collection", collection); - RunTest(@" + RunTest(@" var callCount = 0; var handler = function(sender, eventArgs) { callCount++; } ; collection.add_CollectionChanged(handler); @@ -1266,18 +1266,18 @@ public void ShouldExecuteActionCallbackOnEventChanged() var json = JSON.stringify(Object.keys(handler)); "); - var callCount = (int) _engine.GetValue("callCount").AsNumber(); - Assert.Equal(1, callCount); - Assert.Equal(2, collection.Count); + var callCount = (int) _engine.GetValue("callCount").AsNumber(); + Assert.Equal(1, callCount); + Assert.Equal(2, collection.Count); - // make sure our delegate holder is hidden - Assert.Equal("[]", _engine.Evaluate("json")); - } + // make sure our delegate holder is hidden + Assert.Equal("[]", _engine.Evaluate("json")); + } - [Fact] - public void ShouldUseSystemIO() - { - RunTest(@" + [Fact] + public void ShouldUseSystemIO() + { + RunTest(@" var filename = System.IO.Path.GetTempFileName(); var sw = System.IO.File.CreateText(filename); sw.Write('Hello World'); @@ -1288,244 +1288,244 @@ public void ShouldUseSystemIO() assert(content === 'Hello World'); "); - } + } - [Fact] - public void ShouldImportNamespace() - { - RunTest(@" + [Fact] + public void ShouldImportNamespace() + { + RunTest(@" var Shapes = importNamespace('Shapes'); var circle = new Shapes.Circle(); assert(circle.Radius === 0); assert(circle.Perimeter() === 0); "); - } + } - [Fact] - public void ShouldImportEmptyNamespace() - { - RunTest(""" - var nullSpace = importNamespace(null); - var c1 = new nullSpace.ShapeWithoutNameSpace(); - assert(c1.Perimeter() === 42); - var undefinedSpace = importNamespace(undefined); - var c2 = new undefinedSpace.ShapeWithoutNameSpace(); - assert(c2.Perimeter() === 42); - var defaultSpace = importNamespace(); - var c3 = new defaultSpace.ShapeWithoutNameSpace(); - assert(c3.Perimeter() === 42); - """); - } + [Fact] + public void ShouldImportEmptyNamespace() + { + RunTest(""" + var nullSpace = importNamespace(null); + var c1 = new nullSpace.ShapeWithoutNameSpace(); + assert(c1.Perimeter() === 42); + var undefinedSpace = importNamespace(undefined); + var c2 = new undefinedSpace.ShapeWithoutNameSpace(); + assert(c2.Perimeter() === 42); + var defaultSpace = importNamespace(); + var c3 = new defaultSpace.ShapeWithoutNameSpace(); + assert(c3.Perimeter() === 42); + """); + } - [Fact] - public void ShouldConstructReferenceTypeWithParameters() - { - RunTest(@" + [Fact] + public void ShouldConstructReferenceTypeWithParameters() + { + RunTest(@" var Shapes = importNamespace('Shapes'); var circle = new Shapes.Circle(1); assert(circle.Radius === 1); assert(circle.Perimeter() === Math.PI); "); - } + } - [Fact] - public void ShouldConstructValueTypeWithoutParameters() - { - RunTest(@" + [Fact] + public void ShouldConstructValueTypeWithoutParameters() + { + RunTest(@" var guid = new System.Guid(); assert('00000000-0000-0000-0000-000000000000' === guid.ToString()); "); - } + } - [Fact] - public void ShouldInvokeAFunctionByName() - { - RunTest(@" + [Fact] + public void ShouldInvokeAFunctionByName() + { + RunTest(@" function add(x, y) { return x + y; } "); - Assert.Equal(3, _engine.Invoke("add", 1, 2)); - } + Assert.Equal(3, _engine.Invoke("add", 1, 2)); + } - [Fact] - public void ShouldNotInvokeNonFunctionValue() - { - RunTest(@" + [Fact] + public void ShouldNotInvokeNonFunctionValue() + { + RunTest(@" var x= 10; "); - Assert.Throws(() => _engine.Invoke("x", 1, 2)); - } + Assert.Throws(() => _engine.Invoke("x", 1, 2)); + } - [Fact] - public void CanGetField() + [Fact] + public void CanGetField() + { + var o = new ClassWithField { - var o = new ClassWithField - { - Field = "Mickey Mouse" - }; + Field = "Mickey Mouse" + }; - _engine.SetValue("o", o); + _engine.SetValue("o", o); - RunTest(@" + RunTest(@" assert(o.Field === 'Mickey Mouse'); "); - } + } - [Fact] - public void CanSetField() - { - var o = new ClassWithField(); + [Fact] + public void CanSetField() + { + var o = new ClassWithField(); - _engine.SetValue("o", o); + _engine.SetValue("o", o); - RunTest(@" + RunTest(@" o.Field = 'Mickey Mouse'; assert(o.Field === 'Mickey Mouse'); "); - Assert.Equal("Mickey Mouse", o.Field); - } + Assert.Equal("Mickey Mouse", o.Field); + } - [Fact] - public void CanGetStaticField() - { - RunTest(@" + [Fact] + public void CanGetStaticField() + { + RunTest(@" var domain = importNamespace('Jint.Tests.Runtime.Domain'); var statics = domain.ClassWithStaticFields; assert(statics.Get == 'Get'); "); - } + } - [Fact] - public void CanSetStaticField() - { - RunTest(@" + [Fact] + public void CanSetStaticField() + { + RunTest(@" var domain = importNamespace('Jint.Tests.Runtime.Domain'); var statics = domain.ClassWithStaticFields; statics.Set = 'hello'; assert(statics.Set == 'hello'); "); - Assert.Equal(ClassWithStaticFields.Set, "hello"); - } + Assert.Equal(ClassWithStaticFields.Set, "hello"); + } - [Fact] - public void CanGetStaticAccessor() - { - RunTest(@" + [Fact] + public void CanGetStaticAccessor() + { + RunTest(@" var domain = importNamespace('Jint.Tests.Runtime.Domain'); var statics = domain.ClassWithStaticFields; assert(statics.Getter == 'Getter'); "); - } + } - [Fact] - public void CanSetStaticAccessor() - { - RunTest(@" + [Fact] + public void CanSetStaticAccessor() + { + RunTest(@" var domain = importNamespace('Jint.Tests.Runtime.Domain'); var statics = domain.ClassWithStaticFields; statics.Setter = 'hello'; assert(statics.Setter == 'hello'); "); - Assert.Equal(ClassWithStaticFields.Setter, "hello"); - } + Assert.Equal(ClassWithStaticFields.Setter, "hello"); + } - [Fact] - public void CantSetStaticReadonly() - { - RunTest(@" + [Fact] + public void CantSetStaticReadonly() + { + RunTest(@" var domain = importNamespace('Jint.Tests.Runtime.Domain'); var statics = domain.ClassWithStaticFields; statics.Readonly = 'hello'; assert(statics.Readonly == 'Readonly'); "); - Assert.Equal(ClassWithStaticFields.Readonly, "Readonly"); - } - - [Fact] - public void CanSetCustomConverters() - { - var engine1 = new Engine(); - engine1.SetValue("p", new { Test = true }); - engine1.Execute("var result = p.Test;"); - Assert.True((bool) engine1.GetValue("result").ToObject()); + Assert.Equal(ClassWithStaticFields.Readonly, "Readonly"); + } - var engine2 = new Engine(o => o.AddObjectConverter(new NegateBoolConverter())); - engine2.SetValue("p", new { Test = true }); - engine2.Execute("var result = p.Test;"); - Assert.False((bool) engine2.GetValue("result").ToObject()); - } + [Fact] + public void CanSetCustomConverters() + { + var engine1 = new Engine(); + engine1.SetValue("p", new { Test = true }); + engine1.Execute("var result = p.Test;"); + Assert.True((bool) engine1.GetValue("result").ToObject()); + + var engine2 = new Engine(o => o.AddObjectConverter(new NegateBoolConverter())); + engine2.SetValue("p", new { Test = true }); + engine2.Execute("var result = p.Test;"); + Assert.False((bool) engine2.GetValue("result").ToObject()); + } - [Fact] - public void CanConvertEnumsToString() - { - var engine1 = new Engine(o => o.AddObjectConverter(new EnumsToStringConverter())) - .SetValue("assert", new Action(Assert.True)); - engine1.SetValue("p", new { Comparison = StringComparison.CurrentCulture }); - engine1.Execute("assert(p.Comparison === 'CurrentCulture');"); - engine1.Execute("var result = p.Comparison;"); - Assert.Equal("CurrentCulture", (string) engine1.GetValue("result").ToObject()); - } + [Fact] + public void CanConvertEnumsToString() + { + var engine1 = new Engine(o => o.AddObjectConverter(new EnumsToStringConverter())) + .SetValue("assert", new Action(Assert.True)); + engine1.SetValue("p", new { Comparison = StringComparison.CurrentCulture }); + engine1.Execute("assert(p.Comparison === 'CurrentCulture');"); + engine1.Execute("var result = p.Comparison;"); + Assert.Equal("CurrentCulture", (string) engine1.GetValue("result").ToObject()); + } - [Fact] - public void CanUserIncrementOperator() + [Fact] + public void CanUserIncrementOperator() + { + var p = new Person { - var p = new Person - { - Age = 1 - }; + Age = 1 + }; - _engine.SetValue("p", p); + _engine.SetValue("p", p); - RunTest(@" + RunTest(@" assert(++p.Age === 2); "); - Assert.Equal(2, p.Age); - } + Assert.Equal(2, p.Age); + } - [Fact] - public void CanOverwriteValues() - { - _engine.SetValue("x", 3); - _engine.SetValue("x", 4); + [Fact] + public void CanOverwriteValues() + { + _engine.SetValue("x", 3); + _engine.SetValue("x", 4); - RunTest(@" + RunTest(@" assert(x === 4); "); - } + } - [Fact] - public void ShouldCreateGenericType() - { - RunTest(@" + [Fact] + public void ShouldCreateGenericType() + { + RunTest(@" var ListOfString = System.Collections.Generic.List(System.String); var list = new ListOfString(); list.Add('foo'); list.Add(1); assert(2 === list.Count); "); - } + } - [Fact] - public void EnumComparesByName() + [Fact] + public void EnumComparesByName() + { + var o = new { - var o = new - { - r = Colors.Red, - b = Colors.Blue, - g = Colors.Green, - b2 = Colors.Red - }; + r = Colors.Red, + b = Colors.Blue, + g = Colors.Green, + b2 = Colors.Red + }; - _engine.SetValue("o", o); - _engine.SetValue("assertFalse", new Action(Assert.False)); + _engine.SetValue("o", o); + _engine.SetValue("assertFalse", new Action(Assert.False)); - RunTest(@" + RunTest(@" var domain = importNamespace('Jint.Tests.Runtime.Domain'); var colors = domain.Colors; assert(o.r === colors.Red); @@ -1533,19 +1533,19 @@ public void EnumComparesByName() assert(o.b === colors.Blue); assertFalse(o.b2 === colors.Blue); "); - } + } - [Fact] - public void ShouldSetEnumProperty() + [Fact] + public void ShouldSetEnumProperty() + { + var s = new Circle { - var s = new Circle - { - Color = Colors.Red - }; + Color = Colors.Red + }; - _engine.SetValue("s", s); + _engine.SetValue("s", s); - RunTest(@" + RunTest(@" var domain = importNamespace('Jint.Tests.Runtime.Domain'); var colors = domain.Colors; @@ -1553,190 +1553,190 @@ public void ShouldSetEnumProperty() assert(s.Color === colors.Blue); "); - _engine.SetValue("s", s); + _engine.SetValue("s", s); - RunTest(@" + RunTest(@" s.Color = colors.Blue | colors.Green; assert(s.Color === colors.Blue | colors.Green); "); - Assert.Equal(Colors.Blue | Colors.Green, s.Color); - } + Assert.Equal(Colors.Blue | Colors.Green, s.Color); + } - private enum TestEnumInt32 : int - { - None, - One = 1, - Min = int.MaxValue, - Max = int.MaxValue - } + private enum TestEnumInt32 : int + { + None, + One = 1, + Min = int.MaxValue, + Max = int.MaxValue + } - private enum TestEnumUInt32 : uint - { - None, - One = 1, - Min = uint.MaxValue, - Max = uint.MaxValue - } + private enum TestEnumUInt32 : uint + { + None, + One = 1, + Min = uint.MaxValue, + Max = uint.MaxValue + } - private enum TestEnumInt64 : long - { - None, - One = 1, - Min = long.MaxValue, - Max = long.MaxValue - } + private enum TestEnumInt64 : long + { + None, + One = 1, + Min = long.MaxValue, + Max = long.MaxValue + } - private enum TestEnumUInt64 : ulong - { - None, - One = 1, - Min = ulong.MaxValue, - Max = ulong.MaxValue - } + private enum TestEnumUInt64 : ulong + { + None, + One = 1, + Min = ulong.MaxValue, + Max = ulong.MaxValue + } - private void TestEnum(T enumValue) - { - var i = Convert.ChangeType(enumValue, Enum.GetUnderlyingType(typeof(T))); - var s = Convert.ToString(i, CultureInfo.InvariantCulture); - var o = new Tuple(enumValue); - _engine.SetValue("o", o); - RunTest("assert(o.Item1 === " + s + ");"); - } + private void TestEnum(T enumValue) + { + var i = Convert.ChangeType(enumValue, Enum.GetUnderlyingType(typeof(T))); + var s = Convert.ToString(i, CultureInfo.InvariantCulture); + var o = new Tuple(enumValue); + _engine.SetValue("o", o); + RunTest("assert(o.Item1 === " + s + ");"); + } - [Fact] - public void ShouldWorkWithEnumInt32() - { - TestEnum(TestEnumInt32.None); - TestEnum(TestEnumInt32.One); - TestEnum(TestEnumInt32.Min); - TestEnum(TestEnumInt32.Max); - } + [Fact] + public void ShouldWorkWithEnumInt32() + { + TestEnum(TestEnumInt32.None); + TestEnum(TestEnumInt32.One); + TestEnum(TestEnumInt32.Min); + TestEnum(TestEnumInt32.Max); + } - [Fact] - public void ShouldWorkWithEnumUInt32() - { - TestEnum(TestEnumUInt32.None); - TestEnum(TestEnumUInt32.One); - TestEnum(TestEnumUInt32.Min); - TestEnum(TestEnumUInt32.Max); - } + [Fact] + public void ShouldWorkWithEnumUInt32() + { + TestEnum(TestEnumUInt32.None); + TestEnum(TestEnumUInt32.One); + TestEnum(TestEnumUInt32.Min); + TestEnum(TestEnumUInt32.Max); + } - [Fact] - public void ShouldWorkWithEnumInt64() - { - TestEnum(TestEnumInt64.None); - TestEnum(TestEnumInt64.One); - TestEnum(TestEnumInt64.Min); - TestEnum(TestEnumInt64.Max); - } + [Fact] + public void ShouldWorkWithEnumInt64() + { + TestEnum(TestEnumInt64.None); + TestEnum(TestEnumInt64.One); + TestEnum(TestEnumInt64.Min); + TestEnum(TestEnumInt64.Max); + } - [Fact] - public void ShouldWorkWithEnumUInt64() - { - TestEnum(TestEnumUInt64.None); - TestEnum(TestEnumUInt64.One); - TestEnum(TestEnumUInt64.Min); - TestEnum(TestEnumUInt64.Max); - } + [Fact] + public void ShouldWorkWithEnumUInt64() + { + TestEnum(TestEnumUInt64.None); + TestEnum(TestEnumUInt64.One); + TestEnum(TestEnumUInt64.Min); + TestEnum(TestEnumUInt64.Max); + } - [Fact] - public void EnumIsConvertedToNumber() + [Fact] + public void EnumIsConvertedToNumber() + { + var o = new { - var o = new - { - r = Colors.Red, - b = Colors.Blue, - g = Colors.Green - }; + r = Colors.Red, + b = Colors.Blue, + g = Colors.Green + }; - _engine.SetValue("o", o); + _engine.SetValue("o", o); - RunTest(@" + RunTest(@" assert(o.r === 0); assert(o.g === 1); assert(o.b === 10); "); - } + } - [Fact] - public void ShouldConvertToEnum() + [Fact] + public void ShouldConvertToEnum() + { + var s = new Circle { - var s = new Circle - { - Color = Colors.Red - }; + Color = Colors.Red + }; - _engine.SetValue("s", s); + _engine.SetValue("s", s); - RunTest(@" + RunTest(@" assert(s.Color === 0); s.Color = 10; assert(s.Color === 10); "); - _engine.SetValue("s", s); + _engine.SetValue("s", s); - RunTest(@" + RunTest(@" s.Color = 11; assert(s.Color === 11); "); - Assert.Equal(Colors.Blue | Colors.Green, s.Color); - } + Assert.Equal(Colors.Blue | Colors.Green, s.Color); + } - [Fact] - public void ShouldUseExplicitPropertyGetter() - { - _engine.SetValue("c", new Company("ACME")); - Assert.Equal("ACME", _engine.Evaluate("c.Name")); - } + [Fact] + public void ShouldUseExplicitPropertyGetter() + { + _engine.SetValue("c", new Company("ACME")); + Assert.Equal("ACME", _engine.Evaluate("c.Name")); + } - [Fact] - public void ShouldUseExplicitIndexerPropertyGetter() - { - var company = new Company("ACME"); - ((ICompany) company)["Foo"] = "Bar"; - _engine.SetValue("c", company); - Assert.Equal("Bar", _engine.Evaluate("c.Foo")); - } + [Fact] + public void ShouldUseExplicitIndexerPropertyGetter() + { + var company = new Company("ACME"); + ((ICompany) company)["Foo"] = "Bar"; + _engine.SetValue("c", company); + Assert.Equal("Bar", _engine.Evaluate("c.Foo")); + } - [Fact] - public void ShouldUseExplicitPropertySetter() - { - _engine.SetValue("c", new Company("ACME")); - Assert.Equal("Foo", _engine.Evaluate("c.Name = 'Foo'; c.Name;")); - } + [Fact] + public void ShouldUseExplicitPropertySetter() + { + _engine.SetValue("c", new Company("ACME")); + Assert.Equal("Foo", _engine.Evaluate("c.Name = 'Foo'; c.Name;")); + } - [Fact] - public void ShouldUseExplicitIndexerPropertySetter() - { - var company = new Company("ACME"); - ((ICompany) company)["Foo"] = "Bar"; - _engine.SetValue("c", company); + [Fact] + public void ShouldUseExplicitIndexerPropertySetter() + { + var company = new Company("ACME"); + ((ICompany) company)["Foo"] = "Bar"; + _engine.SetValue("c", company); - RunTest(@" + RunTest(@" c.Foo = 'Baz'; assert(c.Foo === 'Baz'); "); - } + } - [Fact] - public void ShouldUseExplicitMethod() - { - _engine.SetValue("c", new Company("ACME")); + [Fact] + public void ShouldUseExplicitMethod() + { + _engine.SetValue("c", new Company("ACME")); - RunTest(@" + RunTest(@" assert(0 === c.CompareTo(c)); "); - } + } - [Fact] - public void ShouldCallInstanceMethodWithParams() - { - _engine.SetValue("a", new A()); + [Fact] + public void ShouldCallInstanceMethodWithParams() + { + _engine.SetValue("a", new A()); - RunTest(@" + RunTest(@" assert(a.Call13('1','2','3') === '1,2,3'); assert(a.Call13('1') === '1'); assert(a.Call13(1) === '1'); @@ -1751,97 +1751,97 @@ public void ShouldCallInstanceMethodWithParams() assert(a.Call13('1','2','3') === a.Call13(['1','2','3'])); "); - } + } - [Fact] - public void ShouldCallInstanceMethodWithJsValueParams() - { - _engine.SetValue("a", new A()); + [Fact] + public void ShouldCallInstanceMethodWithJsValueParams() + { + _engine.SetValue("a", new A()); - RunTest(@" + RunTest(@" assert(a.Call16('1','2','3') === '1,2,3'); assert(a.Call16('1') === '1'); assert(a.Call16(1) === '1'); assert(a.Call16() === ''); assert(a.Call16('1','2','3') === a.Call16(['1','2','3'])); "); - } + } - [Fact] - public void NullValueAsArgumentShouldWork() - { - _engine.SetValue("a", new A()); + [Fact] + public void NullValueAsArgumentShouldWork() + { + _engine.SetValue("a", new A()); - RunTest(@" + RunTest(@" var x = a.Call2(null); assert(x === null); "); - } + } - [Fact] - public void ShouldSetPropertyToNull() - { - var p = new Person { Name = "Mickey" }; - _engine.SetValue("p", p); + [Fact] + public void ShouldSetPropertyToNull() + { + var p = new Person { Name = "Mickey" }; + _engine.SetValue("p", p); - RunTest(@" + RunTest(@" assert(p.Name != null); p.Name = null; assert(p.Name == null); "); - Assert.True(p.Name == null); - } + Assert.True(p.Name == null); + } - [Fact] - public void ShouldCallMethodWithNull() - { - _engine.SetValue("a", new A()); + [Fact] + public void ShouldCallMethodWithNull() + { + _engine.SetValue("a", new A()); - RunTest(@" + RunTest(@" a.Call15(null); var result = a.Call2(null); assert(result == null); "); - } + } - [Fact] - public void ShouldReturnUndefinedProperty() - { - _engine.SetValue("uo", new { foo = "bar" }); - _engine.SetValue("ud", new Dictionary { { "foo", "bar" } }); - _engine.SetValue("ul", new List { "foo", "bar" }); + [Fact] + public void ShouldReturnUndefinedProperty() + { + _engine.SetValue("uo", new { foo = "bar" }); + _engine.SetValue("ud", new Dictionary { { "foo", "bar" } }); + _engine.SetValue("ul", new List { "foo", "bar" }); - RunTest(@" + RunTest(@" assert(!uo.undefinedProperty); assert(!ul[5]); assert(!ud.undefinedProperty); "); - } + } - private class FailingObject2 - { - public int this[int index] => throw new ArgumentException("index is bad", nameof(index)); - } + private class FailingObject2 + { + public int this[int index] => throw new ArgumentException("index is bad", nameof(index)); + } - [Fact] - public void ShouldPropagateIndexerExceptions() - { - var engine = new Engine(); - engine.Execute(@"function f2(obj) { return obj[1]; }"); + [Fact] + public void ShouldPropagateIndexerExceptions() + { + var engine = new Engine(); + engine.Execute(@"function f2(obj) { return obj[1]; }"); - var failingObject = new FailingObject2(); - Assert.Throws(() => engine.Invoke("f2", failingObject)); - } + var failingObject = new FailingObject2(); + Assert.Throws(() => engine.Invoke("f2", failingObject)); + } - [Fact] - public void ShouldAutomaticallyConvertArraysToFindBestInteropResolution() - { - _engine.SetValue("a", new ArrayConverterTestClass()); - _engine.SetValue("item1", new ArrayConverterItem(1)); - _engine.SetValue("item2", new ArrayConverterItem(2)); + [Fact] + public void ShouldAutomaticallyConvertArraysToFindBestInteropResolution() + { + _engine.SetValue("a", new ArrayConverterTestClass()); + _engine.SetValue("item1", new ArrayConverterItem(1)); + _engine.SetValue("item2", new ArrayConverterItem(2)); - RunTest(@" + RunTest(@" assert(a.MethodAcceptsArrayOfInt([false, '1', 2]) === a.MethodAcceptsArrayOfInt([0, 1, 2])); assert(a.MethodAcceptsArrayOfStrings(['1', 2]) === a.MethodAcceptsArrayOfStrings([1, 2])); assert(a.MethodAcceptsArrayOfBool(['1', 0]) === a.MethodAcceptsArrayOfBool([true, false])); @@ -1849,163 +1849,163 @@ public void ShouldAutomaticallyConvertArraysToFindBestInteropResolution() assert(a.MethodAcceptsArrayOfStrings([item1, item2]) === a.MethodAcceptsArrayOfStrings(['1', '2'])); assert(a.MethodAcceptsArrayOfInt([item1, item2]) === a.MethodAcceptsArrayOfInt([1, 2])); "); - } + } - [Fact] - public void ShouldImportNamespaceNestedType() - { - RunTest(@" + [Fact] + public void ShouldImportNamespaceNestedType() + { + RunTest(@" var shapes = importNamespace('Shapes.Circle'); var kinds = shapes.Kind; assert(kinds.Unit === 0); assert(kinds.Ellipse === 1); assert(kinds.Round === 5); "); - } + } - [Fact] - public void ShouldImportNamespaceNestedNestedType() - { - RunTest(@" + [Fact] + public void ShouldImportNamespaceNestedNestedType() + { + RunTest(@" var meta = importNamespace('Shapes.Circle.Meta'); var usages = meta.Usage; assert(usages.Public === 0); assert(usages.Private === 1); assert(usages.Internal === 11); "); - } + } - [Fact] - public void ShouldGetNestedTypeFromParentType() - { - RunTest(@" + [Fact] + public void ShouldGetNestedTypeFromParentType() + { + RunTest(@" var Shapes = importNamespace('Shapes'); var usages = Shapes.Circle.Meta.Usage; assert(usages.Public === 0); assert(usages.Private === 1); assert(usages.Internal === 11); "); - } + } - [Fact] - public void ShouldGetNestedNestedProp() - { - RunTest(@" + [Fact] + public void ShouldGetNestedNestedProp() + { + RunTest(@" var meta = importNamespace('Shapes.Circle'); var m = new meta.Meta(); assert(m.Description === 'descp'); "); - } + } - [Fact] - public void ShouldSetNestedNestedProp() - { - RunTest(@" + [Fact] + public void ShouldSetNestedNestedProp() + { + RunTest(@" var meta = importNamespace('Shapes.Circle'); var m = new meta.Meta(); m.Description = 'hello'; assert(m.Description === 'hello'); "); - } + } - [Fact] - public void CanGetStaticNestedField() - { - RunTest(@" + [Fact] + public void CanGetStaticNestedField() + { + RunTest(@" var domain = importNamespace('Jint.Tests.Runtime.Domain.Nested'); var statics = domain.ClassWithStaticFields; assert(statics.Get == 'Get'); "); - } + } - [Fact] - public void CanSetStaticNestedField() - { - RunTest(@" + [Fact] + public void CanSetStaticNestedField() + { + RunTest(@" var domain = importNamespace('Jint.Tests.Runtime.Domain.Nested'); var statics = domain.ClassWithStaticFields; statics.Set = 'hello'; assert(statics.Set == 'hello'); "); - Assert.Equal(Nested.ClassWithStaticFields.Set, "hello"); - } + Assert.Equal(Nested.ClassWithStaticFields.Set, "hello"); + } - [Fact] - public void CanGetStaticNestedAccessor() - { - RunTest(@" + [Fact] + public void CanGetStaticNestedAccessor() + { + RunTest(@" var domain = importNamespace('Jint.Tests.Runtime.Domain.Nested'); var statics = domain.ClassWithStaticFields; assert(statics.Getter == 'Getter'); "); - } + } - [Fact] - public void CanSetStaticNestedAccessor() - { - RunTest(@" + [Fact] + public void CanSetStaticNestedAccessor() + { + RunTest(@" var domain = importNamespace('Jint.Tests.Runtime.Domain.Nested'); var statics = domain.ClassWithStaticFields; statics.Setter = 'hello'; assert(statics.Setter == 'hello'); "); - Assert.Equal(Nested.ClassWithStaticFields.Setter, "hello"); - } + Assert.Equal(Nested.ClassWithStaticFields.Setter, "hello"); + } - [Fact] - public void CantSetStaticNestedReadonly() - { - RunTest(@" + [Fact] + public void CantSetStaticNestedReadonly() + { + RunTest(@" var domain = importNamespace('Jint.Tests.Runtime.Domain.Nested'); var statics = domain.ClassWithStaticFields; statics.Readonly = 'hello'; assert(statics.Readonly == 'Readonly'); "); - Assert.Equal(Nested.ClassWithStaticFields.Readonly, "Readonly"); - } + Assert.Equal(Nested.ClassWithStaticFields.Readonly, "Readonly"); + } - [Fact] - public void ShouldExecuteFunctionWithValueTypeParameterCorrectly() - { - _engine.SetValue("a", new A()); - // Func - RunTest(@" + [Fact] + public void ShouldExecuteFunctionWithValueTypeParameterCorrectly() + { + _engine.SetValue("a", new A()); + // Func + RunTest(@" assert(a.Call17(function(value){ return value; }) === 17); "); - } + } - [Fact] - public void ShouldExecuteActionWithValueTypeParameterCorrectly() - { - _engine.SetValue("a", new A()); - // Action - RunTest(@" + [Fact] + public void ShouldExecuteActionWithValueTypeParameterCorrectly() + { + _engine.SetValue("a", new A()); + // Action + RunTest(@" a.Call18(function(value){ assert(value === 18); }); "); - } + } - [Fact] - public void ShouldConvertToJsValue() - { - RunTest(@" + [Fact] + public void ShouldConvertToJsValue() + { + RunTest(@" var now = System.DateTime.Now; assert(new String(now) == now.toString()); var zero = System.Int32.MaxValue; assert(new String(zero) == zero.toString()); "); - } + } - [Fact] - public void ShouldNotCatchClrExceptions() - { - var engine = new Engine() - .SetValue("throwMyException", new Action(() => { throw new NotSupportedException(); })) - .SetValue("Thrower", typeof(Thrower)) - .Execute(@" + [Fact] + public void ShouldNotCatchClrExceptions() + { + var engine = new Engine() + .SetValue("throwMyException", new Action(() => { throw new NotSupportedException(); })) + .SetValue("Thrower", typeof(Thrower)) + .Execute(@" function throwException1(){ try { throwMyException(); @@ -2027,19 +2027,19 @@ function throwException2(){ } "); - Assert.ThrowsAny(() => engine.Invoke("throwException1")); - Assert.ThrowsAny(() => engine.Invoke("throwException2")); - } + Assert.ThrowsAny(() => engine.Invoke("throwException1")); + Assert.ThrowsAny(() => engine.Invoke("throwException2")); + } - [Fact] - public void ShouldCatchAllClrExceptions() - { - var exceptionMessage = "myExceptionMessage"; + [Fact] + public void ShouldCatchAllClrExceptions() + { + var exceptionMessage = "myExceptionMessage"; - var engine = new Engine(o => o.CatchClrExceptions()) - .SetValue("throwMyException", new Action(() => { throw new Exception(exceptionMessage); })) - .SetValue("Thrower", typeof(Thrower)) - .Execute(@" + var engine = new Engine(o => o.CatchClrExceptions()) + .SetValue("throwMyException", new Action(() => { throw new Exception(exceptionMessage); })) + .SetValue("Thrower", typeof(Thrower)) + .Execute(@" function throwException1(){ try { throwMyException(); @@ -2061,23 +2061,23 @@ function throwException2(){ } "); - Assert.Equal(engine.Invoke("throwException1").AsString(), exceptionMessage); - Assert.Equal(engine.Invoke("throwException2").AsString(), exceptionMessage); - } + Assert.Equal(engine.Invoke("throwException1").AsString(), exceptionMessage); + Assert.Equal(engine.Invoke("throwException2").AsString(), exceptionMessage); + } - [Fact] - public void ShouldNotCatchClrFromApply() + [Fact] + public void ShouldNotCatchClrFromApply() + { + var engine = new Engine(options => { - var engine = new Engine(options => + options.CatchClrExceptions(e => { - options.CatchClrExceptions(e => - { - Assert.Fail("was called"); - return true; - }); + Assert.Fail("was called"); + return true; }); + }); - engine.Execute(@" + engine.Execute(@" function throwError() { throw new Error(); } @@ -2088,52 +2088,52 @@ function throwError() { // does cause ExceptionDelegateHandler call try { throwError.apply(); } catch {} "); - } + } - private class MemberExceptionTest + private class MemberExceptionTest + { + public MemberExceptionTest(bool throwOnCreate) { - public MemberExceptionTest(bool throwOnCreate) + if (throwOnCreate) { - if (throwOnCreate) - { - throw new InvalidOperationException("thrown as requested"); - } + throw new InvalidOperationException("thrown as requested"); } + } - public JsValue ThrowingProperty1 - { - get => throw new InvalidOperationException(); - set => throw new InvalidOperationException(); - } + public JsValue ThrowingProperty1 + { + get => throw new InvalidOperationException(); + set => throw new InvalidOperationException(); + } - public object ThrowingProperty2 - { - get => throw new InvalidOperationException(); - set => throw new InvalidOperationException(); - } + public object ThrowingProperty2 + { + get => throw new InvalidOperationException(); + set => throw new InvalidOperationException(); + } - public void ThrowingFunction() - { - throw new InvalidOperationException(); - } + public void ThrowingFunction() + { + throw new InvalidOperationException(); } + } - [Fact] - public void ShouldCatchClrMemberExceptions() + [Fact] + public void ShouldCatchClrMemberExceptions() + { + var engine = new Engine(cfg => { - var engine = new Engine(cfg => - { - cfg.AllowClr(); - cfg.CatchClrExceptions(); - }); + cfg.AllowClr(); + cfg.CatchClrExceptions(); + }); - engine.SetValue("assert", new Action(Assert.True)); - engine.SetValue("log", new Action(Console.WriteLine)); - engine.SetValue("create", typeof(MemberExceptionTest)); - engine.SetValue("instance", new MemberExceptionTest(false)); + engine.SetValue("assert", new Action(Assert.True)); + engine.SetValue("log", new Action(Console.WriteLine)); + engine.SetValue("create", typeof(MemberExceptionTest)); + engine.SetValue("instance", new MemberExceptionTest(false)); - // Test calling a constructor that throws an exception - engine.Execute(@" + // Test calling a constructor that throws an exception + engine.Execute(@" try { create(true); @@ -2145,8 +2145,8 @@ public void ShouldCatchClrMemberExceptions() } "); - // Test calling a member function that throws an exception - engine.Execute(@" + // Test calling a member function that throws an exception + engine.Execute(@" try { instance.ThrowingFunction(); @@ -2158,8 +2158,8 @@ public void ShouldCatchClrMemberExceptions() } "); - // Test using a property getter that throws an exception - engine.Execute(@" + // Test using a property getter that throws an exception + engine.Execute(@" try { log(o.ThrowingProperty); @@ -2171,8 +2171,8 @@ public void ShouldCatchClrMemberExceptions() } "); - // Test using a property setter that throws an exception - engine.Execute(@" + // Test using a property setter that throws an exception + engine.Execute(@" try { instance.ThrowingProperty1 = 123; @@ -2193,18 +2193,18 @@ public void ShouldCatchClrMemberExceptions() assert(true); } "); - } + } - [Fact] - public void ShouldCatchSomeExceptions() - { - var exceptionMessage = "myExceptionMessage"; + [Fact] + public void ShouldCatchSomeExceptions() + { + var exceptionMessage = "myExceptionMessage"; - var engine = new Engine(o => o.CatchClrExceptions(e => e is NotSupportedException)) - .SetValue("throwMyException1", new Action(() => { throw new NotSupportedException(exceptionMessage); })) - .SetValue("throwMyException2", new Action(() => { throw new ArgumentNullException(); })) - .SetValue("Thrower", typeof(Thrower)) - .Execute(@" + var engine = new Engine(o => o.CatchClrExceptions(e => e is NotSupportedException)) + .SetValue("throwMyException1", new Action(() => { throw new NotSupportedException(exceptionMessage); })) + .SetValue("throwMyException2", new Action(() => { throw new ArgumentNullException(); })) + .SetValue("Thrower", typeof(Thrower)) + .Execute(@" function throwException1(){ try { throwMyException1(); @@ -2246,227 +2246,227 @@ function throwException4(){ } "); - Assert.Equal(engine.Invoke("throwException1").AsString(), exceptionMessage); - Assert.Throws(() => engine.Invoke("throwException2")); - Assert.Equal(engine.Invoke("throwException3").AsString(), exceptionMessage); - Assert.Throws(() => engine.Invoke("throwException4")); - } + Assert.Equal(engine.Invoke("throwException1").AsString(), exceptionMessage); + Assert.Throws(() => engine.Invoke("throwException2")); + Assert.Equal(engine.Invoke("throwException3").AsString(), exceptionMessage); + Assert.Throws(() => engine.Invoke("throwException4")); + } - [Fact] - public void ArrayFromShouldConvertListToArrayLike() + [Fact] + public void ArrayFromShouldConvertListToArrayLike() + { + var list = new List { - var list = new List - { - new Person { Name = "Mike" }, - new Person { Name = "Mika" } - }; - _engine.SetValue("a", list); + new Person { Name = "Mike" }, + new Person { Name = "Mika" } + }; + _engine.SetValue("a", list); - RunTest(@" + RunTest(@" var arr = new Array(a); assert(arr.length === 2); assert(arr[0].Name === 'Mike'); assert(arr[1].Name === 'Mika'); "); - RunTest(@" + RunTest(@" var arr = Array.from(a); assert(arr.length === 2); assert(arr[0].Name === 'Mike'); assert(arr[1].Name === 'Mika'); "); - } + } - [Fact] - public void ArrayFromShouldConvertArrayToArrayLike() + [Fact] + public void ArrayFromShouldConvertArrayToArrayLike() + { + var list = new[] { - var list = new[] - { - new Person { Name = "Mike" }, - new Person { Name = "Mika" } - }; - _engine.SetValue("a", list); + new Person { Name = "Mike" }, + new Person { Name = "Mika" } + }; + _engine.SetValue("a", list); - RunTest(@" + RunTest(@" var arr = new Array(a); assert(arr.length === 2); assert(arr[0].Name === 'Mike'); assert(arr[1].Name === 'Mika'); "); - RunTest(@" + RunTest(@" var arr = Array.from(a); assert(arr.length === 2); assert(arr[0].Name === 'Mike'); assert(arr[1].Name === 'Mika'); "); - } + } - [Fact] - public void ArrayFromShouldConvertIEnumerable() + [Fact] + public void ArrayFromShouldConvertIEnumerable() + { + var enumerable = new[] { - var enumerable = new[] - { - new Person { Name = "Mike" }, - new Person { Name = "Mika" } - }.Select(x => x); + new Person { Name = "Mike" }, + new Person { Name = "Mika" } + }.Select(x => x); - _engine.SetValue("a", enumerable); + _engine.SetValue("a", enumerable); - RunTest(@" + RunTest(@" var arr = new Array(a); assert(arr.length === 2); assert(arr[0].Name === 'Mike'); assert(arr[1].Name === 'Mika'); "); - RunTest(@" + RunTest(@" var arr = Array.from(a); assert(arr.length === 2); assert(arr[0].Name === 'Mike'); assert(arr[1].Name === 'Mika'); "); - } + } - [Fact] - public void ShouldBeAbleToPlusAssignStringProperty() - { - var p = new Person(); - var engine = new Engine(); - engine.SetValue("P", p); - engine.Evaluate("P.Name = 'b';"); - engine.Evaluate("P.Name += 'c';"); - Assert.Equal("bc", p.Name); - } + [Fact] + public void ShouldBeAbleToPlusAssignStringProperty() + { + var p = new Person(); + var engine = new Engine(); + engine.SetValue("P", p); + engine.Evaluate("P.Name = 'b';"); + engine.Evaluate("P.Name += 'c';"); + Assert.Equal("bc", p.Name); + } - [Fact] - public void ShouldNotResolveToPrimitiveSymbol() - { - var engine = new Engine(options => - options.AllowClr(typeof(FloatIndexer).GetTypeInfo().Assembly)); - var c = engine.Evaluate(@" + [Fact] + public void ShouldNotResolveToPrimitiveSymbol() + { + var engine = new Engine(options => + options.AllowClr(typeof(FloatIndexer).GetTypeInfo().Assembly)); + var c = engine.Evaluate(@" var domain = importNamespace('Jint.Tests.Runtime.Domain'); return new domain.FloatIndexer(); "); - Assert.NotNull(c.ToString()); - Assert.Equal((uint) 0, c.AsObject().GetLength()); - } + Assert.NotNull(c.ToString()); + Assert.Equal((uint) 0, c.AsObject().GetLength()); + } - private class DictionaryWrapper - { - public IDictionary Values { get; set; } - } + private class DictionaryWrapper + { + public IDictionary Values { get; set; } + } - private class DictionaryTest + private class DictionaryTest + { + public void Test1(IDictionary values) { - public void Test1(IDictionary values) - { - Assert.Equal(1, Convert.ToInt32(values["a"])); - } - - public void Test2(DictionaryWrapper dictionaryObject) - { - Assert.Equal(1, Convert.ToInt32(dictionaryObject.Values["a"])); - } + Assert.Equal(1, Convert.ToInt32(values["a"])); } - [Fact] - public void ShouldBeAbleToPassDictionaryToMethod() + public void Test2(DictionaryWrapper dictionaryObject) { - var engine = new Engine(); - engine.SetValue("dictionaryTest", new DictionaryTest()); - engine.Evaluate("dictionaryTest.test1({ a: 1 });"); + Assert.Equal(1, Convert.ToInt32(dictionaryObject.Values["a"])); } + } - [Fact] - public void ShouldBeAbleToPassDictionaryInObjectToMethod() - { - var engine = new Engine(); - engine.SetValue("dictionaryTest", new DictionaryTest()); - engine.Evaluate("dictionaryTest.test2({ values: { a: 1 } });"); - } + [Fact] + public void ShouldBeAbleToPassDictionaryToMethod() + { + var engine = new Engine(); + engine.SetValue("dictionaryTest", new DictionaryTest()); + engine.Evaluate("dictionaryTest.test1({ a: 1 });"); + } + + [Fact] + public void ShouldBeAbleToPassDictionaryInObjectToMethod() + { + var engine = new Engine(); + engine.SetValue("dictionaryTest", new DictionaryTest()); + engine.Evaluate("dictionaryTest.test2({ values: { a: 1 } });"); + } - [Fact] - public void ShouldSupportSpreadForDictionary() + [Fact] + public void ShouldSupportSpreadForDictionary() + { + var engine = new Engine(); + var state = new Dictionary { - var engine = new Engine(); - var state = new Dictionary - { - { "invoice", new Dictionary { ["number"] = "42" } } - }; - engine.SetValue("state", state); + { "invoice", new Dictionary { ["number"] = "42" } } + }; + engine.SetValue("state", state); - var result = (IDictionary) engine - .Evaluate("({ supplier: 'S1', ...state.invoice })") - .ToObject(); + var result = (IDictionary) engine + .Evaluate("({ supplier: 'S1', ...state.invoice })") + .ToObject(); - Assert.Equal("S1", result["supplier"]); - Assert.Equal("42", result["number"]); - } + Assert.Equal("S1", result["supplier"]); + Assert.Equal("42", result["number"]); + } - [Fact] - public void ShouldSupportSpreadForDictionary2() + [Fact] + public void ShouldSupportSpreadForDictionary2() + { + var engine = new Engine(); + var state = new Dictionary { - var engine = new Engine(); - var state = new Dictionary - { - { "invoice", new Dictionary { ["number"] = "42" } } - }; - engine.SetValue("state", state); + { "invoice", new Dictionary { ["number"] = "42" } } + }; + engine.SetValue("state", state); - var result = (IDictionary) engine - .Execute("function getValue() { return {supplier: 'S1', ...state.invoice}; }") - .Invoke("getValue") - .ToObject(); + var result = (IDictionary) engine + .Execute("function getValue() { return {supplier: 'S1', ...state.invoice}; }") + .Invoke("getValue") + .ToObject(); - Assert.Equal("S1", result["supplier"]); - Assert.Equal("42", result["number"]); - } + Assert.Equal("S1", result["supplier"]); + Assert.Equal("42", result["number"]); + } - [Fact] - public void ShouldSupportSpreadForObject() + [Fact] + public void ShouldSupportSpreadForObject() + { + var engine = new Engine(); + var person = new Person { - var engine = new Engine(); - var person = new Person - { - Name = "Mike", - Age = 20 - }; - engine.SetValue("p", person); - - var result = (IDictionary) engine - .Evaluate("({ supplier: 'S1', ...p })") - .ToObject(); - - Assert.Equal("S1", result["supplier"]); - Assert.Equal("Mike", result["Name"]); - Assert.Equal(20d, result["Age"]); - } + Name = "Mike", + Age = 20 + }; + engine.SetValue("p", person); - [Fact] - public void ShouldBeAbleToJsonStringifyClrObjects() - { - var engine = new Engine(); + var result = (IDictionary) engine + .Evaluate("({ supplier: 'S1', ...p })") + .ToObject(); - engine.Evaluate("var jsObj = { 'key1' :'value1', 'key2' : 'value2' }"); + Assert.Equal("S1", result["supplier"]); + Assert.Equal("Mike", result["Name"]); + Assert.Equal(20d, result["Age"]); + } - engine.SetValue("netObj", new Dictionary - { - { "key1", "value1" }, - { "key2", "value2" } - }); + [Fact] + public void ShouldBeAbleToJsonStringifyClrObjects() + { + var engine = new Engine(); + + engine.Evaluate("var jsObj = { 'key1' :'value1', 'key2' : 'value2' }"); + + engine.SetValue("netObj", new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" } + }); - var jsValue = engine.Evaluate("jsObj['key1']").AsString(); - var clrValue = engine.Evaluate("netObj['key1']").AsString(); - Assert.Equal(jsValue, clrValue); + var jsValue = engine.Evaluate("jsObj['key1']").AsString(); + var clrValue = engine.Evaluate("netObj['key1']").AsString(); + Assert.Equal(jsValue, clrValue); - jsValue = engine.Evaluate("JSON.stringify(jsObj)").AsString(); - clrValue = engine.Evaluate("JSON.stringify(netObj)").AsString(); - Assert.Equal(jsValue, clrValue); + jsValue = engine.Evaluate("JSON.stringify(jsObj)").AsString(); + clrValue = engine.Evaluate("JSON.stringify(netObj)").AsString(); + Assert.Equal(jsValue, clrValue); - // Write properties on screen using showProps function defined on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects - engine.Execute(@"function showProps(obj, objName) { + // Write properties on screen using showProps function defined on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects + engine.Execute(@"function showProps(obj, objName) { var result = """"; for (var i in obj) { if (obj.hasOwnProperty(i)) { @@ -2475,182 +2475,182 @@ public void ShouldBeAbleToJsonStringifyClrObjects() } return result; }"); - jsValue = engine.Evaluate("showProps(jsObj, 'theObject')").AsString(); - clrValue = engine.Evaluate("showProps(jsObj, 'theObject')").AsString(); - Assert.Equal(jsValue, clrValue); - } + jsValue = engine.Evaluate("showProps(jsObj, 'theObject')").AsString(); + clrValue = engine.Evaluate("showProps(jsObj, 'theObject')").AsString(); + Assert.Equal(jsValue, clrValue); + } - [Fact] - public void SettingValueViaIntegerIndexer() - { - var engine = new Engine(cfg => cfg.AllowClr(typeof(FloatIndexer).GetTypeInfo().Assembly)); - engine.SetValue("log", new Action(Console.WriteLine)); - engine.Execute(@" + [Fact] + public void SettingValueViaIntegerIndexer() + { + var engine = new Engine(cfg => cfg.AllowClr(typeof(FloatIndexer).GetTypeInfo().Assembly)); + engine.SetValue("log", new Action(Console.WriteLine)); + engine.Execute(@" var domain = importNamespace('Jint.Tests.Runtime.Domain'); var fia = new domain.IntegerIndexer(); log(fia[0]); "); - Assert.Equal(123, engine.Evaluate("fia[0]").AsNumber()); - engine.Evaluate("fia[0] = 678;"); - Assert.Equal(678, engine.Evaluate("fia[0]").AsNumber()); - } + Assert.Equal(123, engine.Evaluate("fia[0]").AsNumber()); + engine.Evaluate("fia[0] = 678;"); + Assert.Equal(678, engine.Evaluate("fia[0]").AsNumber()); + } - [Fact] - public void IndexingBsonProperties() - { - const string jsonAnimals = @" { ""Animals"": [ { ""Id"": 1, ""Type"": ""Cat"" } ] }"; - var bsonAnimals = BsonDocument.Parse(jsonAnimals); + [Fact] + public void IndexingBsonProperties() + { + const string jsonAnimals = @" { ""Animals"": [ { ""Id"": 1, ""Type"": ""Cat"" } ] }"; + var bsonAnimals = BsonDocument.Parse(jsonAnimals); - _engine.SetValue("animals", bsonAnimals["Animals"]); + _engine.SetValue("animals", bsonAnimals["Animals"]); - // weak equality does conversions from native types - Assert.True(_engine.Evaluate("animals[0].Type == 'Cat'").AsBoolean()); - Assert.True(_engine.Evaluate("animals[0].Id == 1").AsBoolean()); - } + // weak equality does conversions from native types + Assert.True(_engine.Evaluate("animals[0].Type == 'Cat'").AsBoolean()); + Assert.True(_engine.Evaluate("animals[0].Id == 1").AsBoolean()); + } - [Fact] - public void IntegerAndFloatInFunctionOverloads() - { - var engine = new Engine(options => options.AllowClr(GetType().Assembly)); - engine.SetValue("a", new OverLoading()); - Assert.Equal("int-val", engine.Evaluate("a.testFunc(123);").AsString()); - Assert.Equal("float-val", engine.Evaluate("a.testFunc(12.3);").AsString()); - } + [Fact] + public void IntegerAndFloatInFunctionOverloads() + { + var engine = new Engine(options => options.AllowClr(GetType().Assembly)); + engine.SetValue("a", new OverLoading()); + Assert.Equal("int-val", engine.Evaluate("a.testFunc(123);").AsString()); + Assert.Equal("float-val", engine.Evaluate("a.testFunc(12.3);").AsString()); + } - [Fact] - public void TypeConversionWithTemporaryInvalidValuesShouldNotCache() - { - var engine = new Engine(options => options.AllowClr()); - engine.SetValue("IntValueInput", TypeReference.CreateTypeReference(engine, typeof(IntValueInput))); - var ex = Assert.Throws(() => engine.Evaluate("new IntValueInput().testFunc(NaN);").AsString()); - Assert.Equal("No public methods with the specified arguments were found.", ex.Message); + [Fact] + public void TypeConversionWithTemporaryInvalidValuesShouldNotCache() + { + var engine = new Engine(options => options.AllowClr()); + engine.SetValue("IntValueInput", TypeReference.CreateTypeReference(engine, typeof(IntValueInput))); + var ex = Assert.Throws(() => engine.Evaluate("new IntValueInput().testFunc(NaN);").AsString()); + Assert.Equal("No public methods with the specified arguments were found.", ex.Message); - Assert.Equal(123, engine.Evaluate("new IntValueInput().testFunc(123);").AsNumber()); - } + Assert.Equal(123, engine.Evaluate("new IntValueInput().testFunc(123);").AsNumber()); + } - [Fact] - public void CanConvertFloatingPointToIntegerWithoutError() - { - var engine = new Engine(options => options.AllowClr()); - engine.SetValue("IntValueInput", TypeReference.CreateTypeReference(engine, typeof(IntValueInput))); - Assert.Equal(12, engine.Evaluate("new IntValueInput().testFunc(12.3);").AsNumber()); - } + [Fact] + public void CanConvertFloatingPointToIntegerWithoutError() + { + var engine = new Engine(options => options.AllowClr()); + engine.SetValue("IntValueInput", TypeReference.CreateTypeReference(engine, typeof(IntValueInput))); + Assert.Equal(12, engine.Evaluate("new IntValueInput().testFunc(12.3);").AsNumber()); + } - public class IntValueInput - { - public int TestFunc(int value) => value; - } + public class IntValueInput + { + public int TestFunc(int value) => value; + } - public class TestItem - { - public double Cost { get; set; } + public class TestItem + { + public double Cost { get; set; } - public double Age { get; set; } + public double Age { get; set; } - public string Name { get; set; } - } + public string Name { get; set; } + } - public class TestItemList : List + public class TestItemList : List + { + public double Sum(Func calc) { - public double Sum(Func calc) + double rc = 0; + + foreach (var item in this) { - double rc = 0; + rc += calc(item); + } - foreach (var item in this) - { - rc += calc(item); - } + return rc; + } - return rc; - } + public TestItemList Where(Func cond) + { + var rc = new TestItemList(); - public TestItemList Where(Func cond) + foreach (var item in this) { - var rc = new TestItemList(); - - foreach (var item in this) + if (cond(item)) { - if (cond(item)) - { - rc.Add(item); - } + rc.Add(item); } - - return rc; } + + return rc; } + } - [Fact] - public void DelegateCanReturnValue() - { - var engine = new Engine(options => options.AllowClr(GetType().Assembly)); + [Fact] + public void DelegateCanReturnValue() + { + var engine = new Engine(options => options.AllowClr(GetType().Assembly)); - var lst = new TestItemList(); + var lst = new TestItemList(); - lst.Add(new TestItem() { Name = "a", Cost = 1, Age = 10 }); - lst.Add(new TestItem() { Name = "a", Cost = 1, Age = 10 }); - lst.Add(new TestItem() { Name = "b", Cost = 1, Age = 10 }); - lst.Add(new TestItem() { Name = "b", Cost = 1, Age = 10 }); - lst.Add(new TestItem() { Name = "b", Cost = 1, Age = 10 }); + lst.Add(new TestItem() { Name = "a", Cost = 1, Age = 10 }); + lst.Add(new TestItem() { Name = "a", Cost = 1, Age = 10 }); + lst.Add(new TestItem() { Name = "b", Cost = 1, Age = 10 }); + lst.Add(new TestItem() { Name = "b", Cost = 1, Age = 10 }); + lst.Add(new TestItem() { Name = "b", Cost = 1, Age = 10 }); - engine.SetValue("lst", lst); + engine.SetValue("lst", lst); - Assert.Equal(5, engine.Evaluate("lst.Sum(x => x.Cost);").AsNumber()); - Assert.Equal(50, engine.Evaluate("lst.Sum(x => x.Age);").AsNumber()); - Assert.Equal(3, engine.Evaluate("lst.Where(x => x.Name == 'b').Count;").AsNumber()); - Assert.Equal(30, engine.Evaluate("lst.Where(x => x.Name == 'b').Sum(x => x.Age);").AsNumber()); - } + Assert.Equal(5, engine.Evaluate("lst.Sum(x => x.Cost);").AsNumber()); + Assert.Equal(50, engine.Evaluate("lst.Sum(x => x.Age);").AsNumber()); + Assert.Equal(3, engine.Evaluate("lst.Where(x => x.Name == 'b').Count;").AsNumber()); + Assert.Equal(30, engine.Evaluate("lst.Where(x => x.Name == 'b').Sum(x => x.Age);").AsNumber()); + } - [Fact] - public void ObjectWrapperOverridingEquality() - { - // equality same via name - _engine.SetValue("a", new Person { Name = "Name" }); - _engine.SetValue("b", new Person { Name = "Name" }); - _engine.Evaluate("const arr = [ null, a, undefined ];"); + [Fact] + public void ObjectWrapperOverridingEquality() + { + // equality same via name + _engine.SetValue("a", new Person { Name = "Name" }); + _engine.SetValue("b", new Person { Name = "Name" }); + _engine.Evaluate("const arr = [ null, a, undefined ];"); - Assert.Equal(1, _engine.Evaluate("arr.filter(x => x == b).length").AsNumber()); - Assert.Equal(1, _engine.Evaluate("arr.filter(x => x === b).length").AsNumber()); + Assert.Equal(1, _engine.Evaluate("arr.filter(x => x == b).length").AsNumber()); + Assert.Equal(1, _engine.Evaluate("arr.filter(x => x === b).length").AsNumber()); - Assert.True(_engine.Evaluate("arr.find(x => x == b) === a").AsBoolean()); - Assert.True(_engine.Evaluate("arr.find(x => x === b) == a").AsBoolean()); + Assert.True(_engine.Evaluate("arr.find(x => x == b) === a").AsBoolean()); + Assert.True(_engine.Evaluate("arr.find(x => x === b) == a").AsBoolean()); - Assert.Equal(1, _engine.Evaluate("arr.findIndex(x => x == b)").AsNumber()); - Assert.Equal(1, _engine.Evaluate("arr.findIndex(x => x === b)").AsNumber()); + Assert.Equal(1, _engine.Evaluate("arr.findIndex(x => x == b)").AsNumber()); + Assert.Equal(1, _engine.Evaluate("arr.findIndex(x => x === b)").AsNumber()); - Assert.Equal(1, _engine.Evaluate("arr.indexOf(b)").AsNumber()); - Assert.True(_engine.Evaluate("arr.includes(b)").AsBoolean()); - } + Assert.Equal(1, _engine.Evaluate("arr.indexOf(b)").AsNumber()); + Assert.True(_engine.Evaluate("arr.includes(b)").AsBoolean()); + } - [Fact] - public void ObjectWrapperWrappingDictionaryShouldNotBeArrayLike() - { - var wrapper = ObjectWrapper.Create(_engine, new Dictionary()); - Assert.False(wrapper.IsArrayLike); - } + [Fact] + public void ObjectWrapperWrappingDictionaryShouldNotBeArrayLike() + { + var wrapper = ObjectWrapper.Create(_engine, new Dictionary()); + Assert.False(wrapper.IsArrayLike); + } - [Fact] - public void ShouldHandleCyclicReferences() - { - var engine = new Engine(); + [Fact] + public void ShouldHandleCyclicReferences() + { + var engine = new Engine(); - static void Test(string message, object value) - { - Console.WriteLine(message); - } + static void Test(string message, object value) + { + Console.WriteLine(message); + } - engine.Realm.GlobalObject.FastSetDataProperty("global", engine.Realm.GlobalObject); - engine.Realm.GlobalObject.FastSetDataProperty("test", new DelegateWrapper(engine, (Action) Test)); + engine.Realm.GlobalObject.FastSetDataProperty("global", engine.Realm.GlobalObject); + engine.Realm.GlobalObject.FastSetDataProperty("test", new DelegateWrapper(engine, (Action) Test)); - { - var ex = Assert.Throws(() => engine.Realm.GlobalObject.ToObject()); - Assert.Equal("Cyclic reference detected.", ex.Message); - } + { + var ex = Assert.Throws(() => engine.Realm.GlobalObject.ToObject()); + Assert.Equal("Cyclic reference detected.", ex.Message); + } - { - var ex = Assert.Throws(() => - engine.Execute(@" + { + var ex = Assert.Throws(() => + engine.Execute(@" var demo={}; demo.value=1; test('Test 1', demo.value===1); @@ -2658,177 +2658,177 @@ static void Test(string message, object value) demo.demo=demo; test('Test 3', demo); test('Test 4', global);" - ) - ); - Assert.Equal("Cyclic reference detected.", ex.Message); - } + ) + ); + Assert.Equal("Cyclic reference detected.", ex.Message); } + } - [Fact] - public void CanConfigurePropertyNameMatcher() - { - // defaults - var e = new Engine(); - e.SetValue("a", new A()); - Assert.True(e.Evaluate("a.call1").IsObject()); - Assert.True(e.Evaluate("a.Call1").IsObject()); - Assert.True(e.Evaluate("a.CALL1").IsUndefined()); + [Fact] + public void CanConfigurePropertyNameMatcher() + { + // defaults + var e = new Engine(); + e.SetValue("a", new A()); + Assert.True(e.Evaluate("a.call1").IsObject()); + Assert.True(e.Evaluate("a.Call1").IsObject()); + Assert.True(e.Evaluate("a.CALL1").IsUndefined()); - e = new Engine(options => + e = new Engine(options => + { + options.SetTypeResolver(new TypeResolver { - options.SetTypeResolver(new TypeResolver - { - MemberNameComparer = StringComparer.Ordinal - }); + MemberNameComparer = StringComparer.Ordinal }); - e.SetValue("a", new A()); - Assert.True(e.Evaluate("a.call1").IsUndefined()); - Assert.True(e.Evaluate("a.Call1").IsObject()); - Assert.True(e.Evaluate("a.CALL1").IsUndefined()); + }); + e.SetValue("a", new A()); + Assert.True(e.Evaluate("a.call1").IsUndefined()); + Assert.True(e.Evaluate("a.Call1").IsObject()); + Assert.True(e.Evaluate("a.CALL1").IsUndefined()); - e = new Engine(options => + e = new Engine(options => + { + options.SetTypeResolver(new TypeResolver { - options.SetTypeResolver(new TypeResolver - { - MemberNameComparer = StringComparer.OrdinalIgnoreCase - }); + MemberNameComparer = StringComparer.OrdinalIgnoreCase }); - e.SetValue("a", new A()); - Assert.True(e.Evaluate("a.call1").IsObject()); - Assert.True(e.Evaluate("a.Call1").IsObject()); - Assert.True(e.Evaluate("a.CALL1").IsObject()); - } + }); + e.SetValue("a", new A()); + Assert.True(e.Evaluate("a.call1").IsObject()); + Assert.True(e.Evaluate("a.Call1").IsObject()); + Assert.True(e.Evaluate("a.CALL1").IsObject()); + } - [Fact] - public void ShouldNotEnumerateClassMethods() - { - var engine = new Engine(); + [Fact] + public void ShouldNotEnumerateClassMethods() + { + var engine = new Engine(); - var dictionary = new Dictionary - { - { "foo", 5 }, - { "bar", "A string" } - }; - engine.SetValue("dictionary", dictionary); + var dictionary = new Dictionary + { + { "foo", 5 }, + { "bar", "A string" } + }; + engine.SetValue("dictionary", dictionary); - var result = engine.Evaluate($"Object.keys(dictionary).join(',')").AsString(); - Assert.Equal("foo,bar", result); + var result = engine.Evaluate($"Object.keys(dictionary).join(',')").AsString(); + Assert.Equal("foo,bar", result); - engine.Execute("dictionary.ContainsKey('foo')"); - result = engine.Evaluate($"Object.keys(dictionary).join(',')").AsString(); - Assert.Equal("foo,bar", result); - } + engine.Execute("dictionary.ContainsKey('foo')"); + result = engine.Evaluate($"Object.keys(dictionary).join(',')").AsString(); + Assert.Equal("foo,bar", result); + } - [Fact] - public void ShouldNotEnumerateExtensionMethods() - { - var engine = new Engine(cfg => cfg.AddExtensionMethods(typeof(Enumerable))); + [Fact] + public void ShouldNotEnumerateExtensionMethods() + { + var engine = new Engine(cfg => cfg.AddExtensionMethods(typeof(Enumerable))); - var result = engine.Evaluate("Object.keys({ ...[1,2,3] }).join(',')").AsString(); - Assert.Equal("0,1,2", result); + var result = engine.Evaluate("Object.keys({ ...[1,2,3] }).join(',')").AsString(); + Assert.Equal("0,1,2", result); - var script = @" + var script = @" var arr = [1,2,3]; var keys = []; for(var index in arr) keys.push(index); keys.join(','); "; - result = engine.Evaluate(script).ToString(); - Assert.Equal("0,1,2", result); - } + result = engine.Evaluate(script).ToString(); + Assert.Equal("0,1,2", result); + } - [Fact] - public void CanCheckIfCallable() - { - var engine = new Engine(); - engine.Evaluate("var f = () => true;"); + [Fact] + public void CanCheckIfCallable() + { + var engine = new Engine(); + engine.Evaluate("var f = () => true;"); - var result = engine.GetValue("f"); - Assert.True(result.IsCallable); + var result = engine.GetValue("f"); + Assert.True(result.IsCallable); - Assert.True(result.Call(Array.Empty()).AsBoolean()); - Assert.True(result.Call().AsBoolean()); - } + Assert.True(result.Call(Array.Empty()).AsBoolean()); + Assert.True(result.Call().AsBoolean()); + } - [Fact] - public void CanGiveCustomNameToInteropMembers() + [Fact] + public void CanGiveCustomNameToInteropMembers() + { + static IEnumerable MemberNameCreator(MemberInfo prop) { - static IEnumerable MemberNameCreator(MemberInfo prop) + var attributes = prop.GetCustomAttributes(typeof(CustomNameAttribute), true); + if (attributes.Length > 0) { - var attributes = prop.GetCustomAttributes(typeof(CustomNameAttribute), true); - if (attributes.Length > 0) - { - foreach (CustomNameAttribute attribute in attributes) - { - yield return attribute.Name; - } - } - else + foreach (CustomNameAttribute attribute in attributes) { - yield return prop.Name; + yield return attribute.Name; } } - - var customTypeResolver = new TypeResolver - { - MemberNameCreator = MemberNameCreator - }; - - var engine = new Engine(options => + else { - options.SetTypeResolver(customTypeResolver); - options.AddExtensionMethods(typeof(CustomNamedExtensions)); - }); - engine.SetValue("o", new CustomNamed()); - Assert.Equal("StringField", engine.Evaluate("o.jsStringField").AsString()); - Assert.Equal("StringField", engine.Evaluate("o.jsStringField2").AsString()); - Assert.Equal("StaticStringField", engine.Evaluate("o.jsStaticStringField").AsString()); - Assert.Equal("StringProperty", engine.Evaluate("o.jsStringProperty").AsString()); - Assert.Equal("Method", engine.Evaluate("o.jsMethod()").AsString()); - Assert.Equal("StaticMethod", engine.Evaluate("o.jsStaticMethod()").AsString()); - Assert.Equal("InterfaceStringProperty", engine.Evaluate("o.jsInterfaceStringProperty").AsString()); - Assert.Equal("InterfaceMethod", engine.Evaluate("o.jsInterfaceMethod()").AsString()); - Assert.Equal("ExtensionMethod", engine.Evaluate("o.jsExtensionMethod()").AsString()); - - engine.SetValue("XmlHttpRequest", typeof(CustomNamedEnum)); - engine.Evaluate("o.jsEnumProperty = XmlHttpRequest.HEADERS_RECEIVED;"); - Assert.Equal((int) CustomNamedEnum.HeadersReceived, engine.Evaluate("o.jsEnumProperty").AsNumber()); + yield return prop.Name; + } } - [Fact] - public void ShouldBeAbleToHandleInvalidClrConversionViaCatchClrExceptions() + var customTypeResolver = new TypeResolver { - var engine = new Engine(cfg => cfg.CatchClrExceptions()); - engine.SetValue("a", new Person()); - var ex = Assert.Throws(() => engine.Execute("a.age = 'It will not work, but it is normal'")); - Assert.Contains("input string ", ex.Message, StringComparison.OrdinalIgnoreCase); - Assert.Contains(" was not in a correct format", ex.Message, StringComparison.OrdinalIgnoreCase); - } + MemberNameCreator = MemberNameCreator + }; - [Fact] - public void ShouldLetNotSupportedExceptionBubble() + var engine = new Engine(options => { - _engine.SetValue("profile", new Profile()); - var ex = Assert.Throws(() => _engine.Evaluate("profile.AnyProperty")); - Assert.Equal("NOT SUPPORTED", ex.Message); - } + options.SetTypeResolver(customTypeResolver); + options.AddExtensionMethods(typeof(CustomNamedExtensions)); + }); + engine.SetValue("o", new CustomNamed()); + Assert.Equal("StringField", engine.Evaluate("o.jsStringField").AsString()); + Assert.Equal("StringField", engine.Evaluate("o.jsStringField2").AsString()); + Assert.Equal("StaticStringField", engine.Evaluate("o.jsStaticStringField").AsString()); + Assert.Equal("StringProperty", engine.Evaluate("o.jsStringProperty").AsString()); + Assert.Equal("Method", engine.Evaluate("o.jsMethod()").AsString()); + Assert.Equal("StaticMethod", engine.Evaluate("o.jsStaticMethod()").AsString()); + Assert.Equal("InterfaceStringProperty", engine.Evaluate("o.jsInterfaceStringProperty").AsString()); + Assert.Equal("InterfaceMethod", engine.Evaluate("o.jsInterfaceMethod()").AsString()); + Assert.Equal("ExtensionMethod", engine.Evaluate("o.jsExtensionMethod()").AsString()); - [Fact] - public void ShouldBeAbleToUseConvertibleStructAsMethodParameter() - { - _engine.SetValue("test", new DiscordTestClass()); - _engine.SetValue("id", new DiscordId("12345")); + engine.SetValue("XmlHttpRequest", typeof(CustomNamedEnum)); + engine.Evaluate("o.jsEnumProperty = XmlHttpRequest.HEADERS_RECEIVED;"); + Assert.Equal((int) CustomNamedEnum.HeadersReceived, engine.Evaluate("o.jsEnumProperty").AsNumber()); + } - Assert.Equal("12345", _engine.Evaluate("String(id)").AsString()); - Assert.Equal("12345", _engine.Evaluate("test.echo('12345')").AsString()); - Assert.Equal("12345", _engine.Evaluate("test.create(12345)").AsString()); - } + [Fact] + public void ShouldBeAbleToHandleInvalidClrConversionViaCatchClrExceptions() + { + var engine = new Engine(cfg => cfg.CatchClrExceptions()); + engine.SetValue("a", new Person()); + var ex = Assert.Throws(() => engine.Execute("a.age = 'It will not work, but it is normal'")); + Assert.Contains("input string ", ex.Message, StringComparison.OrdinalIgnoreCase); + Assert.Contains(" was not in a correct format", ex.Message, StringComparison.OrdinalIgnoreCase); + } - [Fact] - public void ShouldGetIteratorForListAndDictionary() - { - const string Script = @" + [Fact] + public void ShouldLetNotSupportedExceptionBubble() + { + _engine.SetValue("profile", new Profile()); + var ex = Assert.Throws(() => _engine.Evaluate("profile.AnyProperty")); + Assert.Equal("NOT SUPPORTED", ex.Message); + } + + [Fact] + public void ShouldBeAbleToUseConvertibleStructAsMethodParameter() + { + _engine.SetValue("test", new DiscordTestClass()); + _engine.SetValue("id", new DiscordId("12345")); + + Assert.Equal("12345", _engine.Evaluate("String(id)").AsString()); + Assert.Equal("12345", _engine.Evaluate("test.echo('12345')").AsString()); + Assert.Equal("12345", _engine.Evaluate("test.create(12345)").AsString()); + } + + [Fact] + public void ShouldGetIteratorForListAndDictionary() + { + const string Script = @" var it = collection[Symbol.iterator](); var result = it.next(); var str = """"; @@ -2838,265 +2838,265 @@ public void ShouldGetIteratorForListAndDictionary() } return str;"; - _engine.SetValue("collection", new List { "a", "b", "c" }); - Assert.Equal("abc", _engine.Evaluate(Script)); + _engine.SetValue("collection", new List { "a", "b", "c" }); + Assert.Equal("abc", _engine.Evaluate(Script)); - _engine.SetValue("collection", new Dictionary { { "a", 1 }, { "b", 2 }, { "c", 3 } }); - Assert.Equal("a,1b,2c,3", _engine.Evaluate(Script)); - } + _engine.SetValue("collection", new Dictionary { { "a", 1 }, { "b", 2 }, { "c", 3 } }); + Assert.Equal("a,1b,2c,3", _engine.Evaluate(Script)); + } - [Fact] - public void ShouldNotIntroduceNewPropertiesWhenTraversing() - { - _engine.SetValue("x", new Dictionary { { "First", 1 }, { "Second", 2 } }); + [Fact] + public void ShouldNotIntroduceNewPropertiesWhenTraversing() + { + _engine.SetValue("x", new Dictionary { { "First", 1 }, { "Second", 2 } }); - Assert.Equal("[\"First\",\"Second\"]", _engine.Evaluate("JSON.stringify(Object.keys(x))")); + Assert.Equal("[\"First\",\"Second\"]", _engine.Evaluate("JSON.stringify(Object.keys(x))")); - Assert.Equal("x['First']: 1", _engine.Evaluate("\"x['First']: \" + x['First']")); - Assert.Equal("[\"First\",\"Second\"]", _engine.Evaluate("JSON.stringify(Object.keys(x))")); + Assert.Equal("x['First']: 1", _engine.Evaluate("\"x['First']: \" + x['First']")); + Assert.Equal("[\"First\",\"Second\"]", _engine.Evaluate("JSON.stringify(Object.keys(x))")); - Assert.Equal("x['Third']: undefined", _engine.Evaluate("\"x['Third']: \" + x['Third']")); - Assert.Equal("[\"First\",\"Second\"]", _engine.Evaluate("JSON.stringify(Object.keys(x))")); + Assert.Equal("x['Third']: undefined", _engine.Evaluate("\"x['Third']: \" + x['Third']")); + Assert.Equal("[\"First\",\"Second\"]", _engine.Evaluate("JSON.stringify(Object.keys(x))")); - Assert.Equal(JsValue.Undefined, _engine.Evaluate("x.length")); - Assert.Equal("[\"First\",\"Second\"]", _engine.Evaluate("JSON.stringify(Object.keys(x))")); + Assert.Equal(JsValue.Undefined, _engine.Evaluate("x.length")); + Assert.Equal("[\"First\",\"Second\"]", _engine.Evaluate("JSON.stringify(Object.keys(x))")); - Assert.Equal(2, _engine.Evaluate("x.Count").AsNumber()); - Assert.Equal("[\"First\",\"Second\"]", _engine.Evaluate("JSON.stringify(Object.keys(x))")); + Assert.Equal(2, _engine.Evaluate("x.Count").AsNumber()); + Assert.Equal("[\"First\",\"Second\"]", _engine.Evaluate("JSON.stringify(Object.keys(x))")); - _engine.Evaluate("x.Clear();"); + _engine.Evaluate("x.Clear();"); - Assert.Equal("[]", _engine.Evaluate("JSON.stringify(Object.keys(x))")); + Assert.Equal("[]", _engine.Evaluate("JSON.stringify(Object.keys(x))")); - _engine.Evaluate("x['Fourth'] = 4;"); - Assert.Equal("[\"Fourth\"]", _engine.Evaluate("JSON.stringify(Object.keys(x))")); + _engine.Evaluate("x['Fourth'] = 4;"); + Assert.Equal("[\"Fourth\"]", _engine.Evaluate("JSON.stringify(Object.keys(x))")); - Assert.False(_engine.Evaluate("Object.prototype.hasOwnProperty.call(x, 'Third')").AsBoolean()); - } + Assert.False(_engine.Evaluate("Object.prototype.hasOwnProperty.call(x, 'Third')").AsBoolean()); + } - [Fact] - public void CanConfigureCustomObjectTypeForJsToClrConversion() + [Fact] + public void CanConfigureCustomObjectTypeForJsToClrConversion() + { + var engine = new Engine(options => { - var engine = new Engine(options => - { - options.Interop.CreateClrObject = oi => new Dictionary(); - }); + options.Interop.CreateClrObject = oi => new Dictionary(); + }); - object capture = null; - var callback = (object value) => capture = value; - engine.SetValue("callback", callback); - engine.Evaluate("callback(({'a': 'b'}));"); + object capture = null; + var callback = (object value) => capture = value; + engine.SetValue("callback", callback); + engine.Evaluate("callback(({'a': 'b'}));"); - Assert.IsType>(capture); - var dictionary = (Dictionary) capture; - Assert.Equal("b", dictionary["a"]); - } + Assert.IsType>(capture); + var dictionary = (Dictionary) capture; + Assert.Equal("b", dictionary["a"]); + } - [Fact] - public void ArrayPrototypeIndexOfWithInteropList() - { - var engine = new Jint.Engine(); + [Fact] + public void ArrayPrototypeIndexOfWithInteropList() + { + var engine = new Jint.Engine(); - engine.SetValue("list", new List { "A", "B", "C" }); + engine.SetValue("list", new List { "A", "B", "C" }); - Assert.Equal(1, engine.Evaluate("list.indexOf('B')")); - Assert.Equal(1, engine.Evaluate("list.lastIndexOf('B')")); + Assert.Equal(1, engine.Evaluate("list.indexOf('B')")); + Assert.Equal(1, engine.Evaluate("list.lastIndexOf('B')")); - Assert.Equal(1, engine.Evaluate("Array.prototype.indexOf.call(list, 'B')")); - Assert.Equal(1, engine.Evaluate("Array.prototype.lastIndexOf.call(list, 'B')")); - } + Assert.Equal(1, engine.Evaluate("Array.prototype.indexOf.call(list, 'B')")); + Assert.Equal(1, engine.Evaluate("Array.prototype.lastIndexOf.call(list, 'B')")); + } - [Fact] - public void ArrayPrototypeFindWithInteropList() - { - var engine = new Jint.Engine(); - var list = new List { "A", "B", "C" }; + [Fact] + public void ArrayPrototypeFindWithInteropList() + { + var engine = new Jint.Engine(); + var list = new List { "A", "B", "C" }; - engine.SetValue("list", list); + engine.SetValue("list", list); - Assert.Equal(1, engine.Evaluate("list.findIndex((x) => x === 'B')")); - Assert.Equal('B', engine.Evaluate("list.find((x) => x === 'B')")); - } + Assert.Equal(1, engine.Evaluate("list.findIndex((x) => x === 'B')")); + Assert.Equal('B', engine.Evaluate("list.find((x) => x === 'B')")); + } - [Fact] - public void ArrayPrototypePushWithInteropList() - { - var engine = new Jint.Engine(); + [Fact] + public void ArrayPrototypePushWithInteropList() + { + var engine = new Jint.Engine(); - var list = new List { "A", "B", "C" }; + var list = new List { "A", "B", "C" }; - engine.SetValue("list", list); + engine.SetValue("list", list); - engine.Evaluate("list.push('D')"); - Assert.Equal(4, list.Count); - Assert.Equal("D", list[3]); - Assert.Equal(3, engine.Evaluate("list.lastIndexOf('D')")); - } + engine.Evaluate("list.push('D')"); + Assert.Equal(4, list.Count); + Assert.Equal("D", list[3]); + Assert.Equal(3, engine.Evaluate("list.lastIndexOf('D')")); + } - [Fact] - public void ArrayPrototypePopWithInteropList() - { - var engine = new Jint.Engine(); + [Fact] + public void ArrayPrototypePopWithInteropList() + { + var engine = new Jint.Engine(); - var list = new List { "A", "B", "C" }; - engine.SetValue("list", list); + var list = new List { "A", "B", "C" }; + engine.SetValue("list", list); - Assert.Equal(2, engine.Evaluate("list.lastIndexOf('C')")); - Assert.Equal(3, list.Count); - Assert.Equal("C", engine.Evaluate("list.pop()")); - Assert.Equal(2, list.Count); - Assert.Equal(-1, engine.Evaluate("list.lastIndexOf('C')")); - } + Assert.Equal(2, engine.Evaluate("list.lastIndexOf('C')")); + Assert.Equal(3, list.Count); + Assert.Equal("C", engine.Evaluate("list.pop()")); + Assert.Equal(2, list.Count); + Assert.Equal(-1, engine.Evaluate("list.lastIndexOf('C')")); + } - [Fact] - public void ShouldBeJavaScriptException() - { - var engine = new Engine(cfg => cfg.AllowClr().AllowOperatorOverloading().CatchClrExceptions()); - engine.SetValue("Dimensional", typeof(Dimensional)); + [Fact] + public void ShouldBeJavaScriptException() + { + var engine = new Engine(cfg => cfg.AllowClr().AllowOperatorOverloading().CatchClrExceptions()); + engine.SetValue("Dimensional", typeof(Dimensional)); - engine.Execute(@" + engine.Execute(@" function Eval(param0, param1) { var result = param0 + param1; return result; }"); - // checking working custom type - Assert.Equal(new Dimensional("kg", 90), (new Dimensional("kg", 30) + new Dimensional("kg", 60))); - Assert.Equal(new Dimensional("kg", 90), engine.Invoke("Eval", new object[] { new Dimensional("kg", 30), new Dimensional("kg", 60) }).ToObject()); - Assert.Throws(() => new Dimensional("kg", 30) + new Dimensional("piece", 70)); - - // checking throwing exception in override operator - string errorMsg = string.Empty; - errorMsg = Assert.Throws(() => engine.Invoke("Eval", new object[] { new Dimensional("kg", 30), new Dimensional("piece", 70) })).Message; - Assert.Equal("Dimensionals with different measure types are non-summable", errorMsg); - } + // checking working custom type + Assert.Equal(new Dimensional("kg", 90), (new Dimensional("kg", 30) + new Dimensional("kg", 60))); + Assert.Equal(new Dimensional("kg", 90), engine.Invoke("Eval", new object[] { new Dimensional("kg", 30), new Dimensional("kg", 60) }).ToObject()); + Assert.Throws(() => new Dimensional("kg", 30) + new Dimensional("piece", 70)); + + // checking throwing exception in override operator + string errorMsg = string.Empty; + errorMsg = Assert.Throws(() => engine.Invoke("Eval", new object[] { new Dimensional("kg", 30), new Dimensional("piece", 70) })).Message; + Assert.Equal("Dimensionals with different measure types are non-summable", errorMsg); + } - private class Profile - { - public int AnyProperty => throw new NotSupportedException("NOT SUPPORTED"); - } + private class Profile + { + public int AnyProperty => throw new NotSupportedException("NOT SUPPORTED"); + } - [Fact] - public void GenericParameterResolutionShouldWorkWithNulls() - { - var result = new Engine() - .SetValue("JintCommon", new JintCommon()) - .Evaluate("JintCommon.sum(1, null)") - .AsNumber(); + [Fact] + public void GenericParameterResolutionShouldWorkWithNulls() + { + var result = new Engine() + .SetValue("JintCommon", new JintCommon()) + .Evaluate("JintCommon.sum(1, null)") + .AsNumber(); - Assert.Equal(2, result); - } + Assert.Equal(2, result); + } - public class JintCommon - { - public int Sum(int a, int? b) => a + b.GetValueOrDefault(1); - } + public class JintCommon + { + public int Sum(int a, int? b) => a + b.GetValueOrDefault(1); + } - private delegate void ParamsTestDelegate(params Action[] callbacks); + private delegate void ParamsTestDelegate(params Action[] callbacks); - [Fact] - public void CanUseParamsActions() - { - var engine = new Engine(); - engine.SetValue("print", new Action(_ => { })); - engine.SetValue("callAll", new DelegateWrapper(engine, new ParamsTestDelegate(ParamsTest))); + [Fact] + public void CanUseParamsActions() + { + var engine = new Engine(); + engine.SetValue("print", new Action(_ => { })); + engine.SetValue("callAll", new DelegateWrapper(engine, new ParamsTestDelegate(ParamsTest))); - engine.Execute(@" + engine.Execute(@" callAll( function() { print('a'); }, function() { print('b'); }, function() { print('c'); } ); "); - } + } - private static void ParamsTest(params Action[] callbacks) + private static void ParamsTest(params Action[] callbacks) + { + foreach (var callback in callbacks) { - foreach (var callback in callbacks) - { - callback.Invoke(); - } + callback.Invoke(); } + } - [Fact] - public void ObjectWrapperIdentityIsMaintained() - { - // run in separate method so stack won't keep reference - var reference = RunWeakReferenceTest(); + [Fact] + public void ObjectWrapperIdentityIsMaintained() + { + // run in separate method so stack won't keep reference + var reference = RunWeakReferenceTest(); - GC.Collect(); + GC.Collect(); - // make sure no dangling reference is left - Assert.False(reference.IsAlive); - } + // make sure no dangling reference is left + Assert.False(reference.IsAlive); + } - [MethodImpl(MethodImplOptions.NoInlining)] - private static WeakReference RunWeakReferenceTest() - { - var o = new object(); + [MethodImpl(MethodImplOptions.NoInlining)] + private static WeakReference RunWeakReferenceTest() + { + var o = new object(); - var engine = new Engine() - .SetValue("o", o); + var engine = new Engine() + .SetValue("o", o); - var wrapper1 = (ObjectWrapper) engine.GetValue("o"); - var reference = new WeakReference(wrapper1); + var wrapper1 = (ObjectWrapper) engine.GetValue("o"); + var reference = new WeakReference(wrapper1); - Assert.Same(wrapper1, engine.GetValue("o")); - Assert.Same(o, wrapper1.Target); + Assert.Same(wrapper1, engine.GetValue("o")); + Assert.Same(o, wrapper1.Target); - // reset - engine.Realm.GlobalObject.RemoveOwnProperty("o"); - return reference; - } + // reset + engine.Realm.GlobalObject.RemoveOwnProperty("o"); + return reference; + } - [Fact] - public void CanUseClrFunction() - { - var engine = new Engine(); - engine.SetValue("fn", new ClrFunction(engine, "fn", (_, args) => (JsValue) (args[0].AsInteger() + 1))); + [Fact] + public void CanUseClrFunction() + { + var engine = new Engine(); + engine.SetValue("fn", new ClrFunction(engine, "fn", (_, args) => (JsValue) (args[0].AsInteger() + 1))); - var result = engine.Evaluate("fn(1)"); + var result = engine.Evaluate("fn(1)"); - Assert.Equal(2, result); - } + Assert.Equal(2, result); + } - [Fact] - public void ShouldAllowClrExceptionsThrough() - { - var engine = new Engine(opts => opts.CatchClrExceptions(exc => false)); - engine.SetValue("fn", new ClrFunction(engine, "fn", (_, _) => throw new InvalidOperationException("This is a C# error"))); - const string Source = @" + [Fact] + public void ShouldAllowClrExceptionsThrough() + { + var engine = new Engine(opts => opts.CatchClrExceptions(exc => false)); + engine.SetValue("fn", new ClrFunction(engine, "fn", (_, _) => throw new InvalidOperationException("This is a C# error"))); + const string Source = @" function wrap() { fn(); } wrap(); "; - Assert.Throws(() => engine.Execute(Source)); - } + Assert.Throws(() => engine.Execute(Source)); + } - [Fact] - public void ShouldConvertClrExceptionsToErrors() - { - var engine = new Engine(opts => opts.CatchClrExceptions(exc => exc is InvalidOperationException)); - engine.SetValue("fn", new ClrFunction(engine, "fn", (_, _) => throw new InvalidOperationException("This is a C# error"))); - const string Source = @" + [Fact] + public void ShouldConvertClrExceptionsToErrors() + { + var engine = new Engine(opts => opts.CatchClrExceptions(exc => exc is InvalidOperationException)); + engine.SetValue("fn", new ClrFunction(engine, "fn", (_, _) => throw new InvalidOperationException("This is a C# error"))); + const string Source = @" function wrap() { fn(); } wrap(); "; - var exc = Assert.Throws(() => engine.Execute(Source)); - Assert.Equal(exc.Message, "This is a C# error"); - } + var exc = Assert.Throws(() => engine.Execute(Source)); + Assert.Equal(exc.Message, "This is a C# error"); + } - [Fact] - public void ShouldAllowCatchingConvertedClrExceptions() - { - var engine = new Engine(opts => opts.CatchClrExceptions(exc => exc is InvalidOperationException)); - engine.SetValue("fn", new ClrFunction(engine, "fn", (_, _) => throw new InvalidOperationException("This is a C# error"))); - const string Source = @" + [Fact] + public void ShouldAllowCatchingConvertedClrExceptions() + { + var engine = new Engine(opts => opts.CatchClrExceptions(exc => exc is InvalidOperationException)); + engine.SetValue("fn", new ClrFunction(engine, "fn", (_, _) => throw new InvalidOperationException("This is a C# error"))); + const string Source = @" try { fn(); } catch (e) { @@ -3104,384 +3104,383 @@ public void ShouldAllowCatchingConvertedClrExceptions() } "; - var exc = Assert.Throws(() => engine.Execute(Source)); - Assert.Equal(exc.Message, "Caught: This is a C# error"); - } + var exc = Assert.Throws(() => engine.Execute(Source)); + Assert.Equal(exc.Message, "Caught: This is a C# error"); + } - class Baz + class Baz + { + public int DisposeCalls { get; private set; } + public IEnumerable Enumerator { - public int DisposeCalls { get; private set; } - public IEnumerable Enumerator + get { - get + try { - try - { - for (int i = 0; i < 10; i++) yield return i; - } - finally // finally clause is translated into IDisposable.Dispose in underlying IEnumerator - { - ++DisposeCalls; - } + for (int i = 0; i < 10; i++) yield return i; + } + finally // finally clause is translated into IDisposable.Dispose in underlying IEnumerator + { + ++DisposeCalls; } } } + } - [Fact] - public void ShouldCallEnumeratorDisposeOnNormalTermination() - { - var engine = new Engine(); - var baz = new Baz(); - engine.SetValue("baz", baz); - const string Source = @" + [Fact] + public void ShouldCallEnumeratorDisposeOnNormalTermination() + { + var engine = new Engine(); + var baz = new Baz(); + engine.SetValue("baz", baz); + const string Source = @" for (let i of baz.Enumerator) { }"; - engine.Execute(Source); - Assert.Equal(1, baz.DisposeCalls); - } + engine.Execute(Source); + Assert.Equal(1, baz.DisposeCalls); + } - [Fact] - public void ShouldCallEnumeratorDisposeOnBreak() - { - var engine = new Engine(); - var baz = new Baz(); - engine.SetValue("baz", baz); - const string Source = @" + [Fact] + public void ShouldCallEnumeratorDisposeOnBreak() + { + var engine = new Engine(); + var baz = new Baz(); + engine.SetValue("baz", baz); + const string Source = @" for (let i of baz.Enumerator) { if (i == 2) break; }"; - engine.Execute(Source); - Assert.Equal(1, baz.DisposeCalls); - } + engine.Execute(Source); + Assert.Equal(1, baz.DisposeCalls); + } - [Fact] - public void ShouldCallEnumeratorDisposeOnException() - { - var engine = new Engine(); - var baz = new Baz(); - engine.SetValue("baz", baz); - const string Source = @" + [Fact] + public void ShouldCallEnumeratorDisposeOnException() + { + var engine = new Engine(); + var baz = new Baz(); + engine.SetValue("baz", baz); + const string Source = @" try { for (let i of baz.Enumerator) { if (i == 2) throw 'exception'; } } catch (e) { }"; - engine.Execute(Source); - Assert.Equal(1, baz.DisposeCalls); - } + engine.Execute(Source); + Assert.Equal(1, baz.DisposeCalls); + } - public class PropertyTestClass - { - public object Value; - } + public class PropertyTestClass + { + public object Value; + } - [Fact] - public void PropertiesOfJsObjectPassedToClrShouldBeReadable() - { - _engine.SetValue("MyClass", typeof(PropertyTestClass)); - RunTest(@" + [Fact] + public void PropertiesOfJsObjectPassedToClrShouldBeReadable() + { + _engine.SetValue("MyClass", typeof(PropertyTestClass)); + RunTest(@" var obj = new MyClass(); obj.Value = { foo: 'bar' }; equal('bar', obj.Value.foo); "); - } + } - [Fact] - public void ShouldBeAbleToDeleteDictionaryEntries() - { - var engine = new Engine(options => options.Strict()); + [Fact] + public void ShouldBeAbleToDeleteDictionaryEntries() + { + var engine = new Engine(options => options.Strict()); - var dictionary = new Dictionary - { - { "a", 1 }, - { "b", 2 } - }; + var dictionary = new Dictionary + { + { "a", 1 }, + { "b", 2 } + }; - engine.SetValue("data", dictionary); + engine.SetValue("data", dictionary); - Assert.True(engine.Evaluate("Object.hasOwn(data, 'a')").AsBoolean()); - Assert.True(engine.Evaluate("data['a'] === 1").AsBoolean()); + Assert.True(engine.Evaluate("Object.hasOwn(data, 'a')").AsBoolean()); + Assert.True(engine.Evaluate("data['a'] === 1").AsBoolean()); - engine.Evaluate("data['a'] = 42"); - Assert.True(engine.Evaluate("data['a'] === 42").AsBoolean()); + engine.Evaluate("data['a'] = 42"); + Assert.True(engine.Evaluate("data['a'] === 42").AsBoolean()); - Assert.Equal(42, dictionary["a"]); + Assert.Equal(42, dictionary["a"]); - engine.Execute("delete data['a'];"); + engine.Execute("delete data['a'];"); - Assert.False(engine.Evaluate("Object.hasOwn(data, 'a')").AsBoolean()); - Assert.False(engine.Evaluate("data['a'] === 42").AsBoolean()); + Assert.False(engine.Evaluate("Object.hasOwn(data, 'a')").AsBoolean()); + Assert.False(engine.Evaluate("data['a'] === 42").AsBoolean()); - Assert.False(dictionary.ContainsKey("a")); + Assert.False(dictionary.ContainsKey("a")); - var engineNoWrite = new Engine(options => options.Strict().AllowClrWrite(false)); + var engineNoWrite = new Engine(options => options.Strict().AllowClrWrite(false)); - dictionary = new Dictionary - { - { "a", 1 }, - { "b", 2 } - }; + dictionary = new Dictionary + { + { "a", 1 }, + { "b", 2 } + }; - engineNoWrite.SetValue("data", dictionary); + engineNoWrite.SetValue("data", dictionary); - var ex1 = Assert.Throws(() => engineNoWrite.Evaluate("data['a'] = 42")); - Assert.Equal("Cannot assign to read only property 'a' of System.Collections.Generic.Dictionary`2[System.String,System.Int32]", ex1.Message); + var ex1 = Assert.Throws(() => engineNoWrite.Evaluate("data['a'] = 42")); + Assert.Equal("Cannot assign to read only property 'a' of System.Collections.Generic.Dictionary`2[System.String,System.Int32]", ex1.Message); - // no changes - Assert.True(engineNoWrite.Evaluate("data['a'] === 1").AsBoolean()); + // no changes + Assert.True(engineNoWrite.Evaluate("data['a'] === 1").AsBoolean()); - var ex2 = Assert.Throws(() => engineNoWrite.Execute("delete data['a'];")); - Assert.Equal("Cannot delete property 'a' of System.Collections.Generic.Dictionary`2[System.String,System.Int32]", ex2.Message); - } + var ex2 = Assert.Throws(() => engineNoWrite.Execute("delete data['a'];")); + Assert.Equal("Cannot delete property 'a' of System.Collections.Generic.Dictionary`2[System.String,System.Int32]", ex2.Message); + } - public record RecordTestClass(object Value = null); + public record RecordTestClass(object Value = null); - public class RecordTestClassContext + public class RecordTestClassContext + { + public object Method(RecordTestClass recordTest) { - public object Method(RecordTestClass recordTest) - { - return recordTest.Value; - } + return recordTest.Value; } + } - private class ClassWithIndexerAndProperty - { - public string MyProp { get; } = "from property"; + private class ClassWithIndexerAndProperty + { + public string MyProp { get; } = "from property"; - public string this[string name] => name != nameof(MyProp) ? "from indexer" : null; - } + public string this[string name] => name != nameof(MyProp) ? "from indexer" : null; + } - [Fact] - public void CanToStringObjectWithoutToPrimitiveSymbol() - { - var engine = new Engine(); + [Fact] + public void CanToStringObjectWithoutToPrimitiveSymbol() + { + var engine = new Engine(); - engine.SetValue("obj", new ClassWithIndexerAndProperty()); - Assert.Equal("Jint.Tests.Runtime.InteropTests+ClassWithIndexerAndProperty", engine.Evaluate("obj + ''").AsString()); + engine.SetValue("obj", new ClassWithIndexerAndProperty()); + Assert.Equal("Jint.Tests.Runtime.InteropTests+ClassWithIndexerAndProperty", engine.Evaluate("obj + ''").AsString()); - engine.SetValue("obj", new Company("name")); - Assert.Equal("Jint.Tests.Runtime.Domain.Company", engine.Evaluate("obj + ''").AsString()); - } + engine.SetValue("obj", new Company("name")); + Assert.Equal("Jint.Tests.Runtime.Domain.Company", engine.Evaluate("obj + ''").AsString()); + } - [Fact] - public void CanConstructOptionalRecordClass() - { - _engine.SetValue("Context", new RecordTestClassContext()); - Assert.Equal(null, _engine.Evaluate("Context.method({});").ToObject()); - Assert.Equal(5, _engine.Evaluate("Context.method({ value: 5 });").AsInteger()); - } + [Fact] + public void CanConstructOptionalRecordClass() + { + _engine.SetValue("Context", new RecordTestClassContext()); + Assert.Equal(null, _engine.Evaluate("Context.method({});").ToObject()); + Assert.Equal(5, _engine.Evaluate("Context.method({ value: 5 });").AsInteger()); + } - [Fact] - public void CanPassDateTimeMinAndMaxViaInterop() - { - var engine = new Engine(cfg => cfg.AllowClrWrite()); + [Fact] + public void CanPassDateTimeMinAndMaxViaInterop() + { + var engine = new Engine(cfg => cfg.AllowClrWrite()); - var dt = DateTime.UtcNow; - engine.SetValue("capture", new Action(o => dt = (DateTime) o)); + var dt = DateTime.UtcNow; + engine.SetValue("capture", new Action(o => dt = (DateTime) o)); - engine.SetValue("minDate", DateTime.MinValue); - engine.Execute("capture(minDate);"); - Assert.Equal(DateTime.MinValue, dt); + engine.SetValue("minDate", DateTime.MinValue); + engine.Execute("capture(minDate);"); + Assert.Equal(DateTime.MinValue, dt); - engine.SetValue("maxDate", DateTime.MaxValue); - engine.Execute("capture(maxDate);"); - Assert.Equal(DateTime.MaxValue, dt); - } + engine.SetValue("maxDate", DateTime.MaxValue); + engine.Execute("capture(maxDate);"); + Assert.Equal(DateTime.MaxValue, dt); + } - private class Container - { - private readonly Child _child = new(); - public Child Child => _child; - public BaseClass Get() => _child; - } + private class Container + { + private readonly Child _child = new(); + public Child Child => _child; + public BaseClass Get() => _child; + } - private class BaseClass { } + private class BaseClass { } - private class Child : BaseClass { } + private class Child : BaseClass { } - [Fact] - public void AccessingBaseTypeShouldBeEqualToAccessingDerivedType() - { - var engine = new Engine().SetValue("container", new Container()); - var res = engine.Evaluate("container.Child === container.Get()"); // These two should be the same object. But this PR makes `container.Get()` return a different object + [Fact] + public void AccessingBaseTypeShouldBeEqualToAccessingDerivedType() + { + var engine = new Engine().SetValue("container", new Container()); + var res = engine.Evaluate("container.Child === container.Get()"); // These two should be the same object. But this PR makes `container.Get()` return a different object - Assert.True(res.AsBoolean()); - } + Assert.True(res.AsBoolean()); + } - public interface IIndexer - { - T this[int index] { get; } - } + public interface IIndexer + { + T this[int index] { get; } + } - public interface ICountable - { - int Count { get; } - } + public interface ICountable + { + int Count { get; } + } + + public interface IStringCollection : IIndexer, ICountable + { + string this[string name] { get; } + } - public interface IStringCollection : IIndexer, ICountable + public class Strings : IStringCollection + { + private readonly string[] _strings; + public Strings(string[] strings) { - string this[string name] { get; } + _strings = strings; } - public class Strings : IStringCollection - { - private readonly string[] _strings; - public Strings(string[] strings) - { - _strings = strings; - } + public string this[string name] => null; + public string this[int index] => _strings[index]; + public int Count => _strings.Length; + } - public string this[string name] => null; - public string this[int index] => _strings[index]; - public int Count => _strings.Length; - } + public class Utils + { + public IStringCollection GetStrings() => new Strings(new [] { "a", "b", "c" }); + } - public class Utils - { - public IStringCollection GetStrings() => new Strings(new [] { "a", "b", "c" }); - } + [Fact] + public void AccessingInterfaceShouldContainExtendedInterfaces() + { + var engine = new Engine(); + engine.SetValue("Utils", new Utils()); + var result = engine.Evaluate("const strings = Utils.GetStrings(); strings.Count;").AsNumber(); + Assert.Equal(3, result); + } - [Fact] - public void AccessingInterfaceShouldContainExtendedInterfaces() - { - var engine = new Engine(); - engine.SetValue("Utils", new Utils()); - var result = engine.Evaluate("const strings = Utils.GetStrings(); strings.Count;").AsNumber(); - Assert.Equal(3, result); - } + [Fact] + public void IntegerIndexerIfPreferredOverStringIndexerWhenFound() + { + var engine = new Engine(); + engine.SetValue("Utils", new Utils()); + var result = engine.Evaluate("const strings = Utils.GetStrings(); strings[2];"); + Assert.Equal("c", result); + } - [Fact] - public void IntegerIndexerIfPreferredOverStringIndexerWhenFound() - { - var engine = new Engine(); - engine.SetValue("Utils", new Utils()); - var result = engine.Evaluate("const strings = Utils.GetStrings(); strings[2];"); - Assert.Equal("c", result); - } + [Fact] + public void CanDestructureInteropTargetMethod() + { + var engine = new Engine(); + engine.SetValue("test", new Utils()); + var result = engine.Evaluate("const { getStrings } = test; getStrings().Count;"); + Assert.Equal(3, result); + } - [Fact] - public void CanDestructureInteropTargetMethod() + private class MetadataWrapper : IDictionary + { + public IEnumerator> GetEnumerator() => throw new NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public void Add(KeyValuePair item) => throw new NotImplementedException(); + public void Clear() => throw new NotImplementedException(); + public bool Contains(KeyValuePair item) => throw new NotImplementedException(); + public void CopyTo(KeyValuePair[] array, int arrayIndex) => throw new NotImplementedException(); + public bool Remove(KeyValuePair item) => throw new NotImplementedException(); + public int Count { get; set; } + public bool IsReadOnly { get; set; } + public bool ContainsKey(string key) => throw new NotImplementedException(); + public void Add(string key, object value) => throw new NotImplementedException(); + public bool Remove(string key) => throw new NotImplementedException(); + public bool TryGetValue(string key, out object value) { - var engine = new Engine(); - engine.SetValue("test", new Utils()); - var result = engine.Evaluate("const { getStrings } = test; getStrings().Count;"); - Assert.Equal(3, result); + value = "from-wrapper"; + return true; } - private class MetadataWrapper : IDictionary - { - public IEnumerator> GetEnumerator() => throw new NotImplementedException(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public void Add(KeyValuePair item) => throw new NotImplementedException(); - public void Clear() => throw new NotImplementedException(); - public bool Contains(KeyValuePair item) => throw new NotImplementedException(); - public void CopyTo(KeyValuePair[] array, int arrayIndex) => throw new NotImplementedException(); - public bool Remove(KeyValuePair item) => throw new NotImplementedException(); - public int Count { get; set; } - public bool IsReadOnly { get; set; } - public bool ContainsKey(string key) => throw new NotImplementedException(); - public void Add(string key, object value) => throw new NotImplementedException(); - public bool Remove(string key) => throw new NotImplementedException(); - public bool TryGetValue(string key, out object value) + public object this[string key] + { + get => "from-wrapper"; + set { - value = "from-wrapper"; - return true; } + } - public object this[string key] - { - get => "from-wrapper"; - set - { - } - } + public ICollection Keys { get; set; } + public ICollection Values { get; set; } + } - public ICollection Keys { get; set; } - public ICollection Values { get; set; } - } + private class ShadowedGetter : IReadOnlyDictionary + { + private Dictionary _dictionary = new(); - private class ShadowedGetter : IReadOnlyDictionary + public void SetInitial(object value, string key) { - private Dictionary _dictionary = new(); - - public void SetInitial(object value, string key) - { - _dictionary[key] = value; - } + _dictionary[key] = value; + } - public IEnumerator> GetEnumerator() => throw new NotImplementedException(); + public IEnumerator> GetEnumerator() => throw new NotImplementedException(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public int Count { get; } - public bool ContainsKey(string key) => _dictionary.ContainsKey(key); + public int Count { get; } + public bool ContainsKey(string key) => _dictionary.ContainsKey(key); - public bool TryGetValue(string key, out object value) => _dictionary.TryGetValue(key, out value); + public bool TryGetValue(string key, out object value) => _dictionary.TryGetValue(key, out value); - public object this[string key] + public object this[string key] + { + get { - get - { - _dictionary.TryGetValue(key, out var value); - return value; - } + _dictionary.TryGetValue(key, out var value); + return value; } - - public IEnumerable Keys { get; set; } - public IEnumerable Values { get; set; } } - private class ShadowingSetter : ShadowedGetter + public IEnumerable Keys { get; set; } + public IEnumerable Values { get; set; } + } + + private class ShadowingSetter : ShadowedGetter + { + public Dictionary Metadata { - public Dictionary Metadata + set { - set - { - SetInitial(new MetadataWrapper(), "metadata"); - } + SetInitial(new MetadataWrapper(), "metadata"); } } + } - [Fact] - public void CanSelectShadowedPropertiesBasedOnReadableAndWritable() + [Fact] + public void CanSelectShadowedPropertiesBasedOnReadableAndWritable() + { + var engine = new Engine(); + engine.SetValue("test", new ShadowingSetter { - var engine = new Engine(); - engine.SetValue("test", new ShadowingSetter - { - Metadata = null - }); + Metadata = null + }); - engine.Evaluate("test.metadata['abc'] = 123"); - var result = engine.Evaluate("test.metadata['abc']"); - Assert.Equal("from-wrapper", result); - } + engine.Evaluate("test.metadata['abc'] = 123"); + var result = engine.Evaluate("test.metadata['abc']"); + Assert.Equal("from-wrapper", result); + } - [Fact] - public void ShouldRespectConcreteGenericReturnTypes() + [Fact] + public void ShouldRespectConcreteGenericReturnTypes() + { + var engine = new Engine(opt => { - var engine = new Engine(opt => - { - opt.AddExtensionMethods(typeof(Enumerable)); // Allow LINQ extension methods. - }); + opt.AddExtensionMethods(typeof(Enumerable)); // Allow LINQ extension methods. + }); - var result = new List(); - void Debug(object o) - { - result.Add($"{o?.GetType().Name ?? "null"}: {o ?? "null"}"); - } + var result = new List(); + void Debug(object o) + { + result.Add($"{o?.GetType().Name ?? "null"}: {o ?? "null"}"); + } - engine.SetValue("debug", Debug); - engine.SetValue("dict", new Dictionary { ["test"] = "val" }); + engine.SetValue("debug", Debug); + engine.SetValue("dict", new Dictionary { ["test"] = "val" }); - engine.Execute("var t = dict.last(kvp => { debug(kvp); debug(kvp.key); return kvp.key != null; } );"); - engine.Execute("debug(t); debug(t.key);"); + engine.Execute("var t = dict.last(kvp => { debug(kvp); debug(kvp.key); return kvp.key != null; } );"); + engine.Execute("debug(t); debug(t.key);"); - Assert.Equal(4, result.Count); - Assert.Equal("KeyValuePair`2: [test, val]", result[0]); - Assert.Equal("String: test", result[1]); - Assert.Equal("KeyValuePair`2: [test, val]", result[2]); - Assert.Equal("String: test", result[3]); - } + Assert.Equal(4, result.Count); + Assert.Equal("KeyValuePair`2: [test, val]", result[0]); + Assert.Equal("String: test", result[1]); + Assert.Equal("KeyValuePair`2: [test, val]", result[2]); + Assert.Equal("String: test", result[3]); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/JsValueConversionTests.cs b/Jint.Tests/Runtime/JsValueConversionTests.cs index b6c991b90d..b5dbd89b95 100644 --- a/Jint.Tests/Runtime/JsValueConversionTests.cs +++ b/Jint.Tests/Runtime/JsValueConversionTests.cs @@ -1,202 +1,201 @@ using Jint.Native; using Jint.Runtime; -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class JsValueConversionTests { - public class JsValueConversionTests + private readonly Engine _engine; + + public JsValueConversionTests() + { + _engine = new Engine(); + } + + [Fact] + public void ShouldBeAnArray() + { + var value = new JsArray(_engine); + Assert.Equal(false, value.IsBoolean()); + Assert.Equal(true, value.IsArray()); + Assert.Equal(false, value.IsDate()); + Assert.Equal(false, value.IsNull()); + Assert.Equal(false, value.IsNumber()); + Assert.Equal(true, value.IsObject()); + Assert.Equal(false, value.IsPrimitive()); + Assert.Equal(false, value.IsRegExp()); + Assert.Equal(false, value.IsString()); + Assert.Equal(false, value.IsUndefined()); + + Assert.Equal(true, value.AsArray() != null); + } + + [Fact] + public void ShouldBeABoolean() + { + var value = JsBoolean.True; + Assert.Equal(true, value.IsBoolean()); + Assert.Equal(false, value.IsArray()); + Assert.Equal(false, value.IsDate()); + Assert.Equal(false, value.IsNull()); + Assert.Equal(false, value.IsNumber()); + Assert.Equal(false, value.IsObject()); + Assert.Equal(true, value.IsPrimitive()); + Assert.Equal(false, value.IsRegExp()); + Assert.Equal(false, value.IsString()); + Assert.Equal(false, value.IsUndefined()); + + Assert.Equal(true, value.AsBoolean()); + } + + [Fact] + public void ShouldBeADate() + { + var value = new JsDate(_engine, double.NaN); + Assert.Equal(false, value.IsBoolean()); + Assert.Equal(false, value.IsArray()); + Assert.Equal(true, value.IsDate()); + Assert.Equal(false, value.IsNull()); + Assert.Equal(false, value.IsNumber()); + Assert.Equal(true, value.IsObject()); + Assert.Equal(false, value.IsPrimitive()); + Assert.Equal(false, value.IsRegExp()); + Assert.Equal(false, value.IsString()); + Assert.Equal(false, value.IsUndefined()); + + Assert.Equal(true, value.AsDate() != null); + } + + [Fact] + public void ShouldBeNull() + { + var value = JsValue.Null; + Assert.Equal(false, value.IsBoolean()); + Assert.Equal(false, value.IsArray()); + Assert.Equal(false, value.IsDate()); + Assert.Equal(true, value.IsNull()); + Assert.Equal(false, value.IsNumber()); + Assert.Equal(false, value.IsObject()); + Assert.Equal(true, value.IsPrimitive()); + Assert.Equal(false, value.IsRegExp()); + Assert.Equal(false, value.IsString()); + Assert.Equal(false, value.IsUndefined()); + } + + [Fact] + public void ShouldBeANumber() + { + var value = new JsNumber(2); + Assert.Equal(false, value.IsBoolean()); + Assert.Equal(false, value.IsArray()); + Assert.Equal(false, value.IsDate()); + Assert.Equal(false, value.IsNull()); + Assert.Equal(true, value.IsNumber()); + Assert.Equal(2, value.AsNumber()); + Assert.Equal(false, value.IsObject()); + Assert.Equal(true, value.IsPrimitive()); + Assert.Equal(false, value.IsRegExp()); + Assert.Equal(false, value.IsString()); + Assert.Equal(false, value.IsUndefined()); + } + + [Fact] + public void ShouldBeAnObject() { - private readonly Engine _engine; - - public JsValueConversionTests() - { - _engine = new Engine(); - } - - [Fact] - public void ShouldBeAnArray() - { - var value = new JsArray(_engine); - Assert.Equal(false, value.IsBoolean()); - Assert.Equal(true, value.IsArray()); - Assert.Equal(false, value.IsDate()); - Assert.Equal(false, value.IsNull()); - Assert.Equal(false, value.IsNumber()); - Assert.Equal(true, value.IsObject()); - Assert.Equal(false, value.IsPrimitive()); - Assert.Equal(false, value.IsRegExp()); - Assert.Equal(false, value.IsString()); - Assert.Equal(false, value.IsUndefined()); - - Assert.Equal(true, value.AsArray() != null); - } - - [Fact] - public void ShouldBeABoolean() - { - var value = JsBoolean.True; - Assert.Equal(true, value.IsBoolean()); - Assert.Equal(false, value.IsArray()); - Assert.Equal(false, value.IsDate()); - Assert.Equal(false, value.IsNull()); - Assert.Equal(false, value.IsNumber()); - Assert.Equal(false, value.IsObject()); - Assert.Equal(true, value.IsPrimitive()); - Assert.Equal(false, value.IsRegExp()); - Assert.Equal(false, value.IsString()); - Assert.Equal(false, value.IsUndefined()); - - Assert.Equal(true, value.AsBoolean()); - } - - [Fact] - public void ShouldBeADate() - { - var value = new JsDate(_engine, double.NaN); - Assert.Equal(false, value.IsBoolean()); - Assert.Equal(false, value.IsArray()); - Assert.Equal(true, value.IsDate()); - Assert.Equal(false, value.IsNull()); - Assert.Equal(false, value.IsNumber()); - Assert.Equal(true, value.IsObject()); - Assert.Equal(false, value.IsPrimitive()); - Assert.Equal(false, value.IsRegExp()); - Assert.Equal(false, value.IsString()); - Assert.Equal(false, value.IsUndefined()); - - Assert.Equal(true, value.AsDate() != null); - } - - [Fact] - public void ShouldBeNull() - { - var value = JsValue.Null; - Assert.Equal(false, value.IsBoolean()); - Assert.Equal(false, value.IsArray()); - Assert.Equal(false, value.IsDate()); - Assert.Equal(true, value.IsNull()); - Assert.Equal(false, value.IsNumber()); - Assert.Equal(false, value.IsObject()); - Assert.Equal(true, value.IsPrimitive()); - Assert.Equal(false, value.IsRegExp()); - Assert.Equal(false, value.IsString()); - Assert.Equal(false, value.IsUndefined()); - } - - [Fact] - public void ShouldBeANumber() - { - var value = new JsNumber(2); - Assert.Equal(false, value.IsBoolean()); - Assert.Equal(false, value.IsArray()); - Assert.Equal(false, value.IsDate()); - Assert.Equal(false, value.IsNull()); - Assert.Equal(true, value.IsNumber()); - Assert.Equal(2, value.AsNumber()); - Assert.Equal(false, value.IsObject()); - Assert.Equal(true, value.IsPrimitive()); - Assert.Equal(false, value.IsRegExp()); - Assert.Equal(false, value.IsString()); - Assert.Equal(false, value.IsUndefined()); - } - - [Fact] - public void ShouldBeAnObject() - { - var value = new JsObject(_engine); - Assert.Equal(false, value.IsBoolean()); - Assert.Equal(false, value.IsArray()); - Assert.Equal(false, value.IsDate()); - Assert.Equal(false, value.IsNull()); - Assert.Equal(false, value.IsNumber()); - Assert.Equal(true, value.IsObject()); - Assert.Equal(true, value.AsObject() != null); - Assert.Equal(false, value.IsPrimitive()); - Assert.Equal(false, value.IsRegExp()); - Assert.Equal(false, value.IsString()); - Assert.Equal(false, value.IsUndefined()); - } - - [Fact] - public void ShouldBeARegExp() - { - var value = new JsRegExp(_engine); - Assert.Equal(false, value.IsBoolean()); - Assert.Equal(false, value.IsArray()); - Assert.Equal(false, value.IsDate()); - Assert.Equal(false, value.IsNull()); - Assert.Equal(false, value.IsNumber()); - Assert.Equal(true, value.IsObject()); - Assert.Equal(false, value.IsPrimitive()); - Assert.Equal(true, value.IsRegExp()); - Assert.Equal(true, value.AsRegExp() != null); - Assert.Equal(false, value.IsString()); - Assert.Equal(false, value.IsUndefined()); - } - - [Fact] - public void ShouldBeAString() - { - var value = new JsString("a"); - Assert.Equal(false, value.IsBoolean()); - Assert.Equal(false, value.IsArray()); - Assert.Equal(false, value.IsDate()); - Assert.Equal(false, value.IsNull()); - Assert.Equal(false, value.IsNumber()); - Assert.Equal(false, value.IsObject()); - Assert.Equal(true, value.IsPrimitive()); - Assert.Equal(false, value.IsRegExp()); - Assert.Equal(true, value.IsString()); - Assert.Equal("a", value.AsString()); - Assert.Equal(false, value.IsUndefined()); - } - - [Fact] - public void ShouldBeUndefined() - { - var value = JsValue.Undefined; - Assert.Equal(false, value.IsBoolean()); - Assert.Equal(false, value.IsArray()); - Assert.Equal(false, value.IsDate()); - Assert.Equal(false, value.IsNull()); - Assert.Equal(false, value.IsNumber()); - Assert.Equal(false, value.IsObject()); - Assert.Equal(true, value.IsPrimitive()); - Assert.Equal(false, value.IsRegExp()); - Assert.Equal(false, value.IsString()); - Assert.Equal(true, value.IsUndefined()); - } - - [Fact] - public void ShouldConvertArrayBuffer() - { - var value = _engine.Evaluate("new Uint8Array([102, 111, 111]).buffer"); - Assert.Equal(true, value.IsArrayBuffer()); - Assert.Equal([102, 111, 111], value.AsArrayBuffer()); - Assert.Equal([102, 111, 111], value.ToObject() as byte[]); - - (value as JsArrayBuffer).DetachArrayBuffer(); - - Assert.Equal(true, value.IsArrayBuffer()); - Assert.Equal(null, value.AsArrayBuffer()); - Assert.Throws(value.ToObject); - Assert.Throws(JsValue.Undefined.AsArrayBuffer); - } - - [Fact] - public void ShouldConvertDataView() - { - var value = _engine.Evaluate("new DataView(new Uint8Array([102, 102, 111, 111, 111]).buffer, 1, 3)"); - - Assert.Equal(true, value.IsDataView()); - Assert.Equal([102, 111, 111], value.AsDataView()); - Assert.Equal([102, 111, 111], value.ToObject() as byte[]); - - (value as JsDataView)._viewedArrayBuffer.DetachArrayBuffer(); - - Assert.Equal(true, value.IsDataView()); - Assert.Equal(null, value.AsDataView()); - Assert.Throws(value.ToObject); - Assert.Throws(JsValue.Undefined.AsDataView); - } + var value = new JsObject(_engine); + Assert.Equal(false, value.IsBoolean()); + Assert.Equal(false, value.IsArray()); + Assert.Equal(false, value.IsDate()); + Assert.Equal(false, value.IsNull()); + Assert.Equal(false, value.IsNumber()); + Assert.Equal(true, value.IsObject()); + Assert.Equal(true, value.AsObject() != null); + Assert.Equal(false, value.IsPrimitive()); + Assert.Equal(false, value.IsRegExp()); + Assert.Equal(false, value.IsString()); + Assert.Equal(false, value.IsUndefined()); + } + + [Fact] + public void ShouldBeARegExp() + { + var value = new JsRegExp(_engine); + Assert.Equal(false, value.IsBoolean()); + Assert.Equal(false, value.IsArray()); + Assert.Equal(false, value.IsDate()); + Assert.Equal(false, value.IsNull()); + Assert.Equal(false, value.IsNumber()); + Assert.Equal(true, value.IsObject()); + Assert.Equal(false, value.IsPrimitive()); + Assert.Equal(true, value.IsRegExp()); + Assert.Equal(true, value.AsRegExp() != null); + Assert.Equal(false, value.IsString()); + Assert.Equal(false, value.IsUndefined()); + } + + [Fact] + public void ShouldBeAString() + { + var value = new JsString("a"); + Assert.Equal(false, value.IsBoolean()); + Assert.Equal(false, value.IsArray()); + Assert.Equal(false, value.IsDate()); + Assert.Equal(false, value.IsNull()); + Assert.Equal(false, value.IsNumber()); + Assert.Equal(false, value.IsObject()); + Assert.Equal(true, value.IsPrimitive()); + Assert.Equal(false, value.IsRegExp()); + Assert.Equal(true, value.IsString()); + Assert.Equal("a", value.AsString()); + Assert.Equal(false, value.IsUndefined()); + } + + [Fact] + public void ShouldBeUndefined() + { + var value = JsValue.Undefined; + Assert.Equal(false, value.IsBoolean()); + Assert.Equal(false, value.IsArray()); + Assert.Equal(false, value.IsDate()); + Assert.Equal(false, value.IsNull()); + Assert.Equal(false, value.IsNumber()); + Assert.Equal(false, value.IsObject()); + Assert.Equal(true, value.IsPrimitive()); + Assert.Equal(false, value.IsRegExp()); + Assert.Equal(false, value.IsString()); + Assert.Equal(true, value.IsUndefined()); + } + + [Fact] + public void ShouldConvertArrayBuffer() + { + var value = _engine.Evaluate("new Uint8Array([102, 111, 111]).buffer"); + Assert.Equal(true, value.IsArrayBuffer()); + Assert.Equal([102, 111, 111], value.AsArrayBuffer()); + Assert.Equal([102, 111, 111], value.ToObject() as byte[]); + + (value as JsArrayBuffer).DetachArrayBuffer(); + + Assert.Equal(true, value.IsArrayBuffer()); + Assert.Equal(null, value.AsArrayBuffer()); + Assert.Throws(value.ToObject); + Assert.Throws(JsValue.Undefined.AsArrayBuffer); + } + + [Fact] + public void ShouldConvertDataView() + { + var value = _engine.Evaluate("new DataView(new Uint8Array([102, 102, 111, 111, 111]).buffer, 1, 3)"); + + Assert.Equal(true, value.IsDataView()); + Assert.Equal([102, 111, 111], value.AsDataView()); + Assert.Equal([102, 111, 111], value.ToObject() as byte[]); + + (value as JsDataView)._viewedArrayBuffer.DetachArrayBuffer(); + + Assert.Equal(true, value.IsDataView()); + Assert.Equal(null, value.AsDataView()); + Assert.Throws(value.ToObject); + Assert.Throws(JsValue.Undefined.AsDataView); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/JsonSerializerTests.cs b/Jint.Tests/Runtime/JsonSerializerTests.cs index 3171ee764f..1e61f62db3 100644 --- a/Jint.Tests/Runtime/JsonSerializerTests.cs +++ b/Jint.Tests/Runtime/JsonSerializerTests.cs @@ -1,163 +1,162 @@ using Jint.Native; using Jint.Native.Json; -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class JsonSerializerTests { - public class JsonSerializerTests + [Fact] + public void CanStringifyBasicTypes() + { + using var engine = new Engine(); + var serializer = new JsonSerializer(engine); + + Assert.Equal("null", serializer.Serialize(JsValue.Null).ToString()); + Assert.Equal("true", serializer.Serialize(JsBoolean.True).ToString()); + Assert.Equal("false", serializer.Serialize(JsBoolean.False).ToString()); + Assert.Equal("\"\"", serializer.Serialize(new JsString("")).ToString()); + Assert.Equal("\"abc\"", serializer.Serialize(new JsString("abc")).ToString()); + Assert.Equal("1", serializer.Serialize(new JsNumber(1)).ToString()); + Assert.Equal("0.5", serializer.Serialize(new JsNumber(0.5)).ToString()); + Assert.Equal("{}", serializer.Serialize(new JsObject(engine)).ToString()); + Assert.Equal("[]", serializer.Serialize(new JsArray(engine)).ToString()); + + Assert.Same(JsValue.Undefined, serializer.Serialize(JsValue.Undefined)); + } + + [Fact] + public void EmptyObjectHasNoLineBreakWithSpaceDefined() { - [Fact] - public void CanStringifyBasicTypes() - { - using var engine = new Engine(); - var serializer = new JsonSerializer(engine); - - Assert.Equal("null", serializer.Serialize(JsValue.Null).ToString()); - Assert.Equal("true", serializer.Serialize(JsBoolean.True).ToString()); - Assert.Equal("false", serializer.Serialize(JsBoolean.False).ToString()); - Assert.Equal("\"\"", serializer.Serialize(new JsString("")).ToString()); - Assert.Equal("\"abc\"", serializer.Serialize(new JsString("abc")).ToString()); - Assert.Equal("1", serializer.Serialize(new JsNumber(1)).ToString()); - Assert.Equal("0.5", serializer.Serialize(new JsNumber(0.5)).ToString()); - Assert.Equal("{}", serializer.Serialize(new JsObject(engine)).ToString()); - Assert.Equal("[]", serializer.Serialize(new JsArray(engine)).ToString()); - - Assert.Same(JsValue.Undefined, serializer.Serialize(JsValue.Undefined)); - } - - [Fact] - public void EmptyObjectHasNoLineBreakWithSpaceDefined() - { - using var engine = new Engine(); - var serializer = new JsonSerializer(engine); - Assert.Equal("{}", serializer.Serialize(new JsObject(engine), JsValue.Undefined, new JsString(" ")).ToString()); - } - - [Fact] - public void EmptyArrayHasNoLineBreakWithSpaceDefined() - { - using var engine = new Engine(); - var serializer = new JsonSerializer(engine); - Assert.Equal("[]", serializer.Serialize(new JsArray(engine), JsValue.Undefined, new JsString(" ")).ToString()); - } - - [Fact] - public void StringCharactersGetEscaped() - { - using var engine = new Engine(); - var serializer = new JsonSerializer(engine); - - string actual = serializer.Serialize(new JsString("\"\\\t\r\n\f\r\b\ud834")).ToString(); - Assert.Equal("\"\\\"\\\\\\t\\r\\n\\f\\r\\b\\ud834\"", actual); - } - - [Fact] - public void JsonStringOutputIsIndentedWhenSpacerDefined() - { - using var engine = new Engine(); - var serializer = new JsonSerializer(engine); - - JsObject instance = new JsObject(engine); - instance["a"] = "b"; - instance["b"] = 2; - instance["c"] = new JsArray(engine, new JsValue[] { new JsNumber(4), new JsNumber(5), new JsNumber(6) }); - instance["d"] = true; - - string actual = serializer.Serialize(instance, JsValue.Undefined, new JsNumber(2)).ToString(); - Assert.Equal("{\n \"a\": \"b\",\n \"b\": 2,\n \"c\": [\n 4,\n 5,\n 6\n ],\n \"d\": true\n}", actual); - } - - [Fact] - public void JsonStringOutputIsCompactWithoutSpacer() - { - using var engine = new Engine(); - var serializer = new JsonSerializer(engine); - - JsObject instance = new JsObject(engine); - instance["a"] = "b"; - instance["b"] = 2; - instance["c"] = new JsArray(engine, new JsValue[] { new JsNumber(4), new JsNumber(5), new JsNumber(6) }); - instance["d"] = true; - - string actual = serializer.Serialize(instance, JsValue.Undefined, JsValue.Undefined).ToString(); - Assert.Equal("{\"a\":\"b\",\"b\":2,\"c\":[4,5,6],\"d\":true}", actual); - } - - [Fact] - public void ArrayWithUndefinedWillBeNull() - { - using var engine = new Engine(); - var serializer = new JsonSerializer(engine); - - JsArray array = new JsArray(engine, new JsValue[] { JsValue.Undefined, new JsNumber(42) }); - string actual = serializer.Serialize(array, JsValue.Undefined, JsValue.Undefined).ToString(); - Assert.Equal("[null,42]", actual); - } - - [Fact] - public void ObjectPropertyWithUndefinedWillBeSkipped() - { - using var engine = new Engine(); - var serializer = new JsonSerializer(engine); - - JsObject instance = new JsObject(engine); - instance["a"] = JsValue.Undefined; - instance["b"] = 42; - string actual = serializer.Serialize(instance, JsValue.Undefined, JsValue.Undefined).ToString(); - Assert.Equal("{\"b\":42}", actual); - } - - [Fact] - public void NonStringObjectKeyWillSerializedAsString() - { - using var engine = new Engine(); - var serializer = new JsonSerializer(engine); - - JsObject instance = new JsObject(engine); - instance[JsValue.Undefined] = 10; - instance[JsValue.Null] = 21; - instance[new JsNumber(10)] = 42; - string actual = serializer.Serialize(instance, JsValue.Undefined, JsValue.Undefined).ToString(); - Assert.Equal("{\"10\":42,\"undefined\":10,\"null\":21}", actual); - } - - [Fact] - public void InfinityAndNaNGetsSerializedAsNull() - { - using var engine = new Engine(); - var serializer = new JsonSerializer(engine); - JsArray array = new JsArray(engine, new JsValue[] { JsNumber.DoubleNegativeInfinity, JsNumber.DoublePositiveInfinity, JsNumber.DoubleNaN }); - string actual = serializer.Serialize(array, JsValue.Undefined, JsValue.Undefined).ToString(); - Assert.Equal("[null,null,null]", actual); - } - - [Fact] - public void ArrayAsReplacedDictatesPropertiesToSerializer() - { - using var engine = new Engine(); - var serializer = new JsonSerializer(engine); - - JsObject instance = new JsObject(engine); - instance["a"] = 21; - instance["b"] = 42; - JsValue replacer = new JsArray(engine, new JsValue[] { new JsString("b"), new JsString("z") }); - string actual = serializer.Serialize(instance, replacer, JsValue.Undefined).ToString(); - Assert.Equal("{\"b\":42}", actual); - } - - [Theory] - [InlineData("test123\n456", "\"test123\\n456\"")] - [InlineData("test123456\n", "\"test123456\\n\"")] - [InlineData("\u0002test\u0002", "\"\\u0002test\\u0002\"")] - [InlineData("\u0002tes\tt\u0002", "\"\\u0002tes\\tt\\u0002\"")] - [InlineData("t\u0002est\u0002", "\"t\\u0002est\\u0002\"")] - [InlineData("test😀123456\n", "\"test😀123456\\n\"")] - public void JsonStringEncodingFormatsContentCorrectly(string inputString, string expectedOutput) - { - using var engine = new Engine(); - var serializer = new JsonSerializer(engine); - - string actual = serializer.Serialize(new JsString(inputString)).ToString(); - Assert.Equal(expectedOutput, actual); - } + using var engine = new Engine(); + var serializer = new JsonSerializer(engine); + Assert.Equal("{}", serializer.Serialize(new JsObject(engine), JsValue.Undefined, new JsString(" ")).ToString()); + } + + [Fact] + public void EmptyArrayHasNoLineBreakWithSpaceDefined() + { + using var engine = new Engine(); + var serializer = new JsonSerializer(engine); + Assert.Equal("[]", serializer.Serialize(new JsArray(engine), JsValue.Undefined, new JsString(" ")).ToString()); + } + + [Fact] + public void StringCharactersGetEscaped() + { + using var engine = new Engine(); + var serializer = new JsonSerializer(engine); + + string actual = serializer.Serialize(new JsString("\"\\\t\r\n\f\r\b\ud834")).ToString(); + Assert.Equal("\"\\\"\\\\\\t\\r\\n\\f\\r\\b\\ud834\"", actual); + } + + [Fact] + public void JsonStringOutputIsIndentedWhenSpacerDefined() + { + using var engine = new Engine(); + var serializer = new JsonSerializer(engine); + + JsObject instance = new JsObject(engine); + instance["a"] = "b"; + instance["b"] = 2; + instance["c"] = new JsArray(engine, new JsValue[] { new JsNumber(4), new JsNumber(5), new JsNumber(6) }); + instance["d"] = true; + + string actual = serializer.Serialize(instance, JsValue.Undefined, new JsNumber(2)).ToString(); + Assert.Equal("{\n \"a\": \"b\",\n \"b\": 2,\n \"c\": [\n 4,\n 5,\n 6\n ],\n \"d\": true\n}", actual); + } + + [Fact] + public void JsonStringOutputIsCompactWithoutSpacer() + { + using var engine = new Engine(); + var serializer = new JsonSerializer(engine); + + JsObject instance = new JsObject(engine); + instance["a"] = "b"; + instance["b"] = 2; + instance["c"] = new JsArray(engine, new JsValue[] { new JsNumber(4), new JsNumber(5), new JsNumber(6) }); + instance["d"] = true; + + string actual = serializer.Serialize(instance, JsValue.Undefined, JsValue.Undefined).ToString(); + Assert.Equal("{\"a\":\"b\",\"b\":2,\"c\":[4,5,6],\"d\":true}", actual); + } + + [Fact] + public void ArrayWithUndefinedWillBeNull() + { + using var engine = new Engine(); + var serializer = new JsonSerializer(engine); + + JsArray array = new JsArray(engine, new JsValue[] { JsValue.Undefined, new JsNumber(42) }); + string actual = serializer.Serialize(array, JsValue.Undefined, JsValue.Undefined).ToString(); + Assert.Equal("[null,42]", actual); + } + + [Fact] + public void ObjectPropertyWithUndefinedWillBeSkipped() + { + using var engine = new Engine(); + var serializer = new JsonSerializer(engine); + + JsObject instance = new JsObject(engine); + instance["a"] = JsValue.Undefined; + instance["b"] = 42; + string actual = serializer.Serialize(instance, JsValue.Undefined, JsValue.Undefined).ToString(); + Assert.Equal("{\"b\":42}", actual); + } + + [Fact] + public void NonStringObjectKeyWillSerializedAsString() + { + using var engine = new Engine(); + var serializer = new JsonSerializer(engine); + + JsObject instance = new JsObject(engine); + instance[JsValue.Undefined] = 10; + instance[JsValue.Null] = 21; + instance[new JsNumber(10)] = 42; + string actual = serializer.Serialize(instance, JsValue.Undefined, JsValue.Undefined).ToString(); + Assert.Equal("{\"10\":42,\"undefined\":10,\"null\":21}", actual); + } + + [Fact] + public void InfinityAndNaNGetsSerializedAsNull() + { + using var engine = new Engine(); + var serializer = new JsonSerializer(engine); + JsArray array = new JsArray(engine, new JsValue[] { JsNumber.DoubleNegativeInfinity, JsNumber.DoublePositiveInfinity, JsNumber.DoubleNaN }); + string actual = serializer.Serialize(array, JsValue.Undefined, JsValue.Undefined).ToString(); + Assert.Equal("[null,null,null]", actual); + } + + [Fact] + public void ArrayAsReplacedDictatesPropertiesToSerializer() + { + using var engine = new Engine(); + var serializer = new JsonSerializer(engine); + + JsObject instance = new JsObject(engine); + instance["a"] = 21; + instance["b"] = 42; + JsValue replacer = new JsArray(engine, new JsValue[] { new JsString("b"), new JsString("z") }); + string actual = serializer.Serialize(instance, replacer, JsValue.Undefined).ToString(); + Assert.Equal("{\"b\":42}", actual); + } + + [Theory] + [InlineData("test123\n456", "\"test123\\n456\"")] + [InlineData("test123456\n", "\"test123456\\n\"")] + [InlineData("\u0002test\u0002", "\"\\u0002test\\u0002\"")] + [InlineData("\u0002tes\tt\u0002", "\"\\u0002tes\\tt\\u0002\"")] + [InlineData("t\u0002est\u0002", "\"t\\u0002est\\u0002\"")] + [InlineData("test😀123456\n", "\"test😀123456\\n\"")] + public void JsonStringEncodingFormatsContentCorrectly(string inputString, string expectedOutput) + { + using var engine = new Engine(); + var serializer = new JsonSerializer(engine); + + string actual = serializer.Serialize(new JsString(inputString)).ToString(); + Assert.Equal(expectedOutput, actual); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/JsonTests.cs b/Jint.Tests/Runtime/JsonTests.cs index 0adb9650c5..e8b896cf92 100644 --- a/Jint.Tests/Runtime/JsonTests.cs +++ b/Jint.Tests/Runtime/JsonTests.cs @@ -3,316 +3,315 @@ using Jint.Native.Object; using Jint.Runtime; -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class JsonTests { - public class JsonTests + [Fact] + public void CanParseTabsInProperties() { - [Fact] - public void CanParseTabsInProperties() - { - var engine = new Engine(); - const string script = @"JSON.parse(""{\""abc\\tdef\"": \""42\""}"");"; - var obj = engine.Evaluate(script).AsObject(); - Assert.True(obj.HasOwnProperty("abc\tdef")); - } + var engine = new Engine(); + const string script = @"JSON.parse(""{\""abc\\tdef\"": \""42\""}"");"; + var obj = engine.Evaluate(script).AsObject(); + Assert.True(obj.HasOwnProperty("abc\tdef")); + } - [Theory] - [InlineData("{\"a\":\"\\\"\"}", "\"")] // " quotation mark - [InlineData("{\"a\":\"\\\\\"}", "\\")] // \ reverse solidus - [InlineData("{\"a\":\"\\/\"}", "/")] // / solidus - [InlineData("{\"a\":\"\\b\"}", "\b")] // backspace - [InlineData("{\"a\":\"\\f\"}", "\f")] // formfeed - [InlineData("{\"a\":\"\\n\"}", "\n")] // linefeed - [InlineData("{\"a\":\"\\r\"}", "\r")] // carriage return - [InlineData("{\"a\":\"\\t\"}", "\t")] // horizontal tab - [InlineData("{\"a\":\"\\u0000\"}", "\0")] - [InlineData("{\"a\":\"\\u0001\"}", "\x01")] - [InlineData("{\"a\":\"\\u0061\"}", "a")] - [InlineData("{\"a\":\"\\u003C\"}", "<")] - [InlineData("{\"a\":\"\\u003E\"}", ">")] - [InlineData("{\"a\":\"\\u003c\"}", "<")] - [InlineData("{\"a\":\"\\u003e\"}", ">")] - public void ShouldParseEscapedCharactersCorrectly(string json, string expectedCharacter) - { - var engine = new Engine(); - var parser = new JsonParser(engine); + [Theory] + [InlineData("{\"a\":\"\\\"\"}", "\"")] // " quotation mark + [InlineData("{\"a\":\"\\\\\"}", "\\")] // \ reverse solidus + [InlineData("{\"a\":\"\\/\"}", "/")] // / solidus + [InlineData("{\"a\":\"\\b\"}", "\b")] // backspace + [InlineData("{\"a\":\"\\f\"}", "\f")] // formfeed + [InlineData("{\"a\":\"\\n\"}", "\n")] // linefeed + [InlineData("{\"a\":\"\\r\"}", "\r")] // carriage return + [InlineData("{\"a\":\"\\t\"}", "\t")] // horizontal tab + [InlineData("{\"a\":\"\\u0000\"}", "\0")] + [InlineData("{\"a\":\"\\u0001\"}", "\x01")] + [InlineData("{\"a\":\"\\u0061\"}", "a")] + [InlineData("{\"a\":\"\\u003C\"}", "<")] + [InlineData("{\"a\":\"\\u003E\"}", ">")] + [InlineData("{\"a\":\"\\u003c\"}", "<")] + [InlineData("{\"a\":\"\\u003e\"}", ">")] + public void ShouldParseEscapedCharactersCorrectly(string json, string expectedCharacter) + { + var engine = new Engine(); + var parser = new JsonParser(engine); - var parsedCharacter = parser.Parse(json).AsObject().Get("a").AsString(); + var parsedCharacter = parser.Parse(json).AsObject().Get("a").AsString(); - Assert.Equal(expectedCharacter, parsedCharacter); - } + Assert.Equal(expectedCharacter, parsedCharacter); + } - [Theory] - [InlineData("{\"a\":1", "Unexpected end of JSON input at position 6")] - [InlineData("{\"a\":1},", "Unexpected token ',' in JSON at position 7")] - [InlineData("{1}", "Unexpected number in JSON at position 1")] - [InlineData("{\"a\" \"a\"}", "Unexpected string in JSON at position 5")] - [InlineData("{true}", "Unexpected token 'true' in JSON at position 1")] - [InlineData("{null}", "Unexpected token 'null' in JSON at position 1")] - [InlineData("{:}", "Unexpected token ':' in JSON at position 1")] - [InlineData("\"\\uah\"", "Expected hexadecimal digit in JSON at position 4")] - [InlineData("0123", "Unexpected token '1' in JSON at position 1")] // leading 0 (octal number) not allowed - [InlineData("1e+A", "Unexpected token 'A' in JSON at position 3")] - [InlineData("truE", "Unexpected token ILLEGAL in JSON at position 0")] - [InlineData("nul", "Unexpected token ILLEGAL in JSON at position 0")] - [InlineData("\"ab\t\"", "Invalid character in JSON at position 3")] // invalid char in string literal - [InlineData("\"ab", "Unexpected end of JSON input at position 3")] // unterminated string literal - [InlineData("alpha", "Unexpected token 'a' in JSON at position 0")] - [InlineData("[1,\na]", "Unexpected token 'a' in JSON at position 4")] // multiline - [InlineData("\x06", "Unexpected token '\x06' in JSON at position 0")] // control char - [InlineData("{\"\\v\":1}", "Unexpected token 'v' in JSON at position 3")] // invalid escape sequence - [InlineData("[,]", "Unexpected token ',' in JSON at position 1")] - [InlineData("{\"key\": ()}", "Unexpected token '(' in JSON at position 8")] - [InlineData(".1", "Unexpected token '.' in JSON at position 0")] - public void ShouldReportHelpfulSyntaxErrorForInvalidJson(string json, string expectedMessage) + [Theory] + [InlineData("{\"a\":1", "Unexpected end of JSON input at position 6")] + [InlineData("{\"a\":1},", "Unexpected token ',' in JSON at position 7")] + [InlineData("{1}", "Unexpected number in JSON at position 1")] + [InlineData("{\"a\" \"a\"}", "Unexpected string in JSON at position 5")] + [InlineData("{true}", "Unexpected token 'true' in JSON at position 1")] + [InlineData("{null}", "Unexpected token 'null' in JSON at position 1")] + [InlineData("{:}", "Unexpected token ':' in JSON at position 1")] + [InlineData("\"\\uah\"", "Expected hexadecimal digit in JSON at position 4")] + [InlineData("0123", "Unexpected token '1' in JSON at position 1")] // leading 0 (octal number) not allowed + [InlineData("1e+A", "Unexpected token 'A' in JSON at position 3")] + [InlineData("truE", "Unexpected token ILLEGAL in JSON at position 0")] + [InlineData("nul", "Unexpected token ILLEGAL in JSON at position 0")] + [InlineData("\"ab\t\"", "Invalid character in JSON at position 3")] // invalid char in string literal + [InlineData("\"ab", "Unexpected end of JSON input at position 3")] // unterminated string literal + [InlineData("alpha", "Unexpected token 'a' in JSON at position 0")] + [InlineData("[1,\na]", "Unexpected token 'a' in JSON at position 4")] // multiline + [InlineData("\x06", "Unexpected token '\x06' in JSON at position 0")] // control char + [InlineData("{\"\\v\":1}", "Unexpected token 'v' in JSON at position 3")] // invalid escape sequence + [InlineData("[,]", "Unexpected token ',' in JSON at position 1")] + [InlineData("{\"key\": ()}", "Unexpected token '(' in JSON at position 8")] + [InlineData(".1", "Unexpected token '.' in JSON at position 0")] + public void ShouldReportHelpfulSyntaxErrorForInvalidJson(string json, string expectedMessage) + { + var engine = new Engine(); + var parser = new JsonParser(engine); + var ex = Assert.ThrowsAny(() => { - var engine = new Engine(); - var parser = new JsonParser(engine); - var ex = Assert.ThrowsAny(() => - { - parser.Parse(json); - }); - - Assert.Equal(expectedMessage, ex.Message); - - var error = ex.Error as Native.Error.ErrorInstance; - Assert.NotNull(error); - Assert.Equal("SyntaxError", error.Get("name")); - } + parser.Parse(json); + }); - [Theory] - [InlineData("[[]]", "[\n []\n]")] - [InlineData("[ { a: [{ x: 0 }], b:[]} ]", - "[\n {\n \"a\": [\n {\n \"x\": 0\n }\n ],\n \"b\": []\n }\n]")] - public void ShouldSerializeWithCorrectIndentation(string script, string expectedJson) - { - var engine = new Engine(); - engine.SetValue("x", engine.Evaluate(script)); + Assert.Equal(expectedMessage, ex.Message); - var result = engine.Evaluate("JSON.stringify(x, null, 2);").AsString(); + var error = ex.Error as Native.Error.ErrorInstance; + Assert.NotNull(error); + Assert.Equal("SyntaxError", error.Get("name")); + } - Assert.Equal(expectedJson, result); - } + [Theory] + [InlineData("[[]]", "[\n []\n]")] + [InlineData("[ { a: [{ x: 0 }], b:[]} ]", + "[\n {\n \"a\": [\n {\n \"x\": 0\n }\n ],\n \"b\": []\n }\n]")] + public void ShouldSerializeWithCorrectIndentation(string script, string expectedJson) + { + var engine = new Engine(); + engine.SetValue("x", engine.Evaluate(script)); + + var result = engine.Evaluate("JSON.stringify(x, null, 2);").AsString(); - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(10)] - [InlineData(100)] - public void ShouldParseArrayIndependentOfLengthInSameOrder(int numberOfElements) + Assert.Equal(expectedJson, result); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + public void ShouldParseArrayIndependentOfLengthInSameOrder(int numberOfElements) + { + string json = $"[{string.Join(",", Enumerable.Range(0, numberOfElements))}]"; + var engine = new Engine(); + var parser = new JsonParser(engine); + + JsValue result = parser.Parse(json); + JsArray array = Assert.IsType(result); + Assert.Equal((uint)numberOfElements, array.Length); + for (int i = 0; i < numberOfElements; i++) { - string json = $"[{string.Join(",", Enumerable.Range(0, numberOfElements))}]"; - var engine = new Engine(); - var parser = new JsonParser(engine); - - JsValue result = parser.Parse(json); - JsArray array = Assert.IsType(result); - Assert.Equal((uint)numberOfElements, array.Length); - for (int i = 0; i < numberOfElements; i++) - { - Assert.Equal(i, (int)array[i].AsNumber()); - } + Assert.Equal(i, (int)array[i].AsNumber()); } + } - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(10)] - [InlineData(20)] - public void ShouldParseStringIndependentOfLength(int stringLength) - { - string generatedString = string.Join("", Enumerable.Range(0, stringLength).Select(index => 'A' + index)); - string json = $"\"{generatedString}\""; - var engine = new Engine(); - var parser = new JsonParser(engine); + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(10)] + [InlineData(20)] + public void ShouldParseStringIndependentOfLength(int stringLength) + { + string generatedString = string.Join("", Enumerable.Range(0, stringLength).Select(index => 'A' + index)); + string json = $"\"{generatedString}\""; + var engine = new Engine(); + var parser = new JsonParser(engine); - string value = parser.Parse(json).AsString(); - Assert.Equal(generatedString, value, StringComparer.Ordinal); - } + string value = parser.Parse(json).AsString(); + Assert.Equal(generatedString, value, StringComparer.Ordinal); + } - [Fact] - public void CanParsePrimitivesCorrectly() - { - var engine = new Engine(); - var parser = new JsonParser(engine); + [Fact] + public void CanParsePrimitivesCorrectly() + { + var engine = new Engine(); + var parser = new JsonParser(engine); - Assert.Same(JsBoolean.True, parser.Parse("true")); - Assert.Same(JsBoolean.False, parser.Parse("false")); - Assert.Same(JsValue.Null, parser.Parse("null")); - } + Assert.Same(JsBoolean.True, parser.Parse("true")); + Assert.Same(JsBoolean.False, parser.Parse("false")); + Assert.Same(JsValue.Null, parser.Parse("null")); + } - [Fact] - public void CanParseNumbersWithAndWithoutSign() - { - var engine = new Engine(); - var parser = new JsonParser(engine); + [Fact] + public void CanParseNumbersWithAndWithoutSign() + { + var engine = new Engine(); + var parser = new JsonParser(engine); - Assert.Equal(-1, (int)parser.Parse("-1").AsNumber()); - Assert.Equal(0, (int)parser.Parse("-0").AsNumber()); - Assert.Equal(0, (int)parser.Parse("0").AsNumber()); - Assert.Equal(1, (int)parser.Parse("1").AsNumber()); - } + Assert.Equal(-1, (int)parser.Parse("-1").AsNumber()); + Assert.Equal(0, (int)parser.Parse("-0").AsNumber()); + Assert.Equal(0, (int)parser.Parse("0").AsNumber()); + Assert.Equal(1, (int)parser.Parse("1").AsNumber()); + } - [Fact] - public void DoesPreservesNumberToMaxSafeInteger() - { - // see https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.max_safe_integer - long maxSafeInteger = 9007199254740991; - var engine = new Engine(); - var parser = new JsonParser(engine); + [Fact] + public void DoesPreservesNumberToMaxSafeInteger() + { + // see https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.max_safe_integer + long maxSafeInteger = 9007199254740991; + var engine = new Engine(); + var parser = new JsonParser(engine); - Assert.Equal(maxSafeInteger, (long)parser.Parse("9007199254740991").AsNumber()); - } + Assert.Equal(maxSafeInteger, (long)parser.Parse("9007199254740991").AsNumber()); + } - [Fact] - public void DoesSupportFractionalNumbers() - { - var engine = new Engine(); - var parser = new JsonParser(engine); + [Fact] + public void DoesSupportFractionalNumbers() + { + var engine = new Engine(); + var parser = new JsonParser(engine); - Assert.Equal(0.1d, parser.Parse("0.1").AsNumber()); - Assert.Equal(1.1d, parser.Parse("1.1").AsNumber()); - Assert.Equal(-1.1d, parser.Parse("-1.1").AsNumber()); - } + Assert.Equal(0.1d, parser.Parse("0.1").AsNumber()); + Assert.Equal(1.1d, parser.Parse("1.1").AsNumber()); + Assert.Equal(-1.1d, parser.Parse("-1.1").AsNumber()); + } - [Fact] - public void DoesSupportScientificNotation() - { - var engine = new Engine(); - var parser = new JsonParser(engine); + [Fact] + public void DoesSupportScientificNotation() + { + var engine = new Engine(); + var parser = new JsonParser(engine); - Assert.Equal(100d, parser.Parse("1E2").AsNumber()); - Assert.Equal(0.01d, parser.Parse("1E-2").AsNumber()); - } + Assert.Equal(100d, parser.Parse("1E2").AsNumber()); + Assert.Equal(0.01d, parser.Parse("1E-2").AsNumber()); + } - [Fact] - public void ThrowsExceptionWhenDepthLimitReachedArrays() - { - string json = GenerateDeepNestedArray(65); + [Fact] + public void ThrowsExceptionWhenDepthLimitReachedArrays() + { + string json = GenerateDeepNestedArray(65); - var engine = new Engine(); - var parser = new JsonParser(engine); + var engine = new Engine(); + var parser = new JsonParser(engine); - JavaScriptException ex = Assert.Throws(() => parser.Parse(json)); - Assert.Equal("Max. depth level of JSON reached at position 64", ex.Message); - } + JavaScriptException ex = Assert.Throws(() => parser.Parse(json)); + Assert.Equal("Max. depth level of JSON reached at position 64", ex.Message); + } - [Fact] - public void ThrowsExceptionWhenDepthLimitReachedObjects() - { - string json = GenerateDeepNestedObject(65); + [Fact] + public void ThrowsExceptionWhenDepthLimitReachedObjects() + { + string json = GenerateDeepNestedObject(65); - var engine = new Engine(); - var parser = new JsonParser(engine); + var engine = new Engine(); + var parser = new JsonParser(engine); - JavaScriptException ex = Assert.Throws(() => parser.Parse(json)); - Assert.Equal("Max. depth level of JSON reached at position 320", ex.Message); - } + JavaScriptException ex = Assert.Throws(() => parser.Parse(json)); + Assert.Equal("Max. depth level of JSON reached at position 320", ex.Message); + } - [Fact] - public void CanParseMultipleNestedObjects() - { - string objectA = GenerateDeepNestedObject(63); - string objectB = GenerateDeepNestedObject(63); - string json = $"{{\"a\":{objectA},\"b\":{objectB} }}"; + [Fact] + public void CanParseMultipleNestedObjects() + { + string objectA = GenerateDeepNestedObject(63); + string objectB = GenerateDeepNestedObject(63); + string json = $"{{\"a\":{objectA},\"b\":{objectB} }}"; - var engine = new Engine(); - var parser = new JsonParser(engine); + var engine = new Engine(); + var parser = new JsonParser(engine); - ObjectInstance parsed = parser.Parse(json).AsObject(); - Assert.True(parsed["a"].IsObject()); - Assert.True(parsed["b"].IsObject()); - } + ObjectInstance parsed = parser.Parse(json).AsObject(); + Assert.True(parsed["a"].IsObject()); + Assert.True(parsed["b"].IsObject()); + } - [Fact] - public void CanParseMultipleNestedArrays() - { - string arrayA = GenerateDeepNestedArray(63); - string arrayB = GenerateDeepNestedArray(63); - string json = $"{{\"a\":{arrayA},\"b\":{arrayB} }}"; + [Fact] + public void CanParseMultipleNestedArrays() + { + string arrayA = GenerateDeepNestedArray(63); + string arrayB = GenerateDeepNestedArray(63); + string json = $"{{\"a\":{arrayA},\"b\":{arrayB} }}"; - var engine = new Engine(); - var parser = new JsonParser(engine); + var engine = new Engine(); + var parser = new JsonParser(engine); - ObjectInstance parsed = parser.Parse(json).AsObject(); - Assert.True(parsed["a"].IsArray()); - Assert.True(parsed["b"].IsArray()); - } + ObjectInstance parsed = parser.Parse(json).AsObject(); + Assert.True(parsed["a"].IsArray()); + Assert.True(parsed["b"].IsArray()); + } - [Fact] - public void ArrayAndObjectDepthNotCountedSeparately() - { - // individual depth is below the default limit, but combined - // a max. depth level is reached. - string innerArray = GenerateDeepNestedArray(40); - string json = GenerateDeepNestedObject(40, innerArray); + [Fact] + public void ArrayAndObjectDepthNotCountedSeparately() + { + // individual depth is below the default limit, but combined + // a max. depth level is reached. + string innerArray = GenerateDeepNestedArray(40); + string json = GenerateDeepNestedObject(40, innerArray); - var engine = new Engine(); - var parser = new JsonParser(engine); + var engine = new Engine(); + var parser = new JsonParser(engine); - JavaScriptException ex = Assert.Throws(() => parser.Parse(json)); - Assert.Equal("Max. depth level of JSON reached at position 224", ex.Message); - } + JavaScriptException ex = Assert.Throws(() => parser.Parse(json)); + Assert.Equal("Max. depth level of JSON reached at position 224", ex.Message); + } - [Fact] - public void CustomMaxDepthOfZeroDisallowsObjects() - { - var engine = new Engine(); - var parser = new JsonParser(engine, maxDepth: 0); + [Fact] + public void CustomMaxDepthOfZeroDisallowsObjects() + { + var engine = new Engine(); + var parser = new JsonParser(engine, maxDepth: 0); - JavaScriptException ex = Assert.Throws(() => parser.Parse("{}")); - Assert.Equal("Max. depth level of JSON reached at position 0", ex.Message); - } + JavaScriptException ex = Assert.Throws(() => parser.Parse("{}")); + Assert.Equal("Max. depth level of JSON reached at position 0", ex.Message); + } - [Fact] - public void CustomMaxDepthOfZeroDisallowsArrays() - { - var engine = new Engine(); - var parser = new JsonParser(engine, maxDepth: 0); + [Fact] + public void CustomMaxDepthOfZeroDisallowsArrays() + { + var engine = new Engine(); + var parser = new JsonParser(engine, maxDepth: 0); - JavaScriptException ex = Assert.Throws(() => parser.Parse("[]")); - Assert.Equal("Max. depth level of JSON reached at position 0", ex.Message); - } + JavaScriptException ex = Assert.Throws(() => parser.Parse("[]")); + Assert.Equal("Max. depth level of JSON reached at position 0", ex.Message); + } - [Fact] - public void MaxDepthDoesNotInfluencePrimitiveValues() - { - var engine = new Engine(); - var parser = new JsonParser(engine, maxDepth: 1); - - ObjectInstance parsed = parser.Parse("{\"a\": 2, \"b\": true, \"c\": null, \"d\": \"test\"}").AsObject(); - Assert.True(parsed["a"].IsNumber()); - Assert.True(parsed["b"].IsBoolean()); - Assert.True(parsed["c"].IsNull()); - Assert.True(parsed["d"].IsString()); - } + [Fact] + public void MaxDepthDoesNotInfluencePrimitiveValues() + { + var engine = new Engine(); + var parser = new JsonParser(engine, maxDepth: 1); + + ObjectInstance parsed = parser.Parse("{\"a\": 2, \"b\": true, \"c\": null, \"d\": \"test\"}").AsObject(); + Assert.True(parsed["a"].IsNumber()); + Assert.True(parsed["b"].IsBoolean()); + Assert.True(parsed["c"].IsNull()); + Assert.True(parsed["d"].IsString()); + } - [Fact] - public void MaxDepthGetsUsedFromEngineOptionsConstraints() - { - var engine = new Engine(options => options.MaxJsonParseDepth(0)); - var parser = new JsonParser(engine); + [Fact] + public void MaxDepthGetsUsedFromEngineOptionsConstraints() + { + var engine = new Engine(options => options.MaxJsonParseDepth(0)); + var parser = new JsonParser(engine); - Assert.Throws(() => parser.Parse("[]")); - } + Assert.Throws(() => parser.Parse("[]")); + } - private static string GenerateDeepNestedArray(int depth) - { - string arrayOpen = new string('[', depth); - string arrayClose = new string(']', depth); - return $"{arrayOpen}{arrayClose}"; - } + private static string GenerateDeepNestedArray(int depth) + { + string arrayOpen = new string('[', depth); + string arrayClose = new string(']', depth); + return $"{arrayOpen}{arrayClose}"; + } - private static string GenerateDeepNestedObject(int depth) - { - return GenerateDeepNestedObject(depth, mostInnerValue: "1"); - } + private static string GenerateDeepNestedObject(int depth) + { + return GenerateDeepNestedObject(depth, mostInnerValue: "1"); + } - private static string GenerateDeepNestedObject(int depth, string mostInnerValue) - { - string objectOpen = string.Concat(Enumerable.Repeat("{\"A\":", depth)); - string objectClose = new string('}', depth); - return $"{objectOpen}{mostInnerValue}{objectClose}"; - } + private static string GenerateDeepNestedObject(int depth, string mostInnerValue) + { + string objectOpen = string.Concat(Enumerable.Repeat("{\"A\":", depth)); + string objectClose = new string('}', depth); + return $"{objectOpen}{mostInnerValue}{objectClose}"; } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/MethodAmbiguityTests.cs b/Jint.Tests/Runtime/MethodAmbiguityTests.cs index 2bb53545fa..2f1fd484d6 100644 --- a/Jint.Tests/Runtime/MethodAmbiguityTests.cs +++ b/Jint.Tests/Runtime/MethodAmbiguityTests.cs @@ -2,16 +2,16 @@ using System.Dynamic; using Jint.Runtime.Interop; -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class MethodAmbiguityTests : IDisposable { - public class MethodAmbiguityTests : IDisposable - { - private readonly Engine _engine; + private readonly Engine _engine; - public MethodAmbiguityTests() - { - _engine = new Engine(cfg => cfg - .AllowOperatorOverloading()) + public MethodAmbiguityTests() + { + _engine = new Engine(cfg => cfg + .AllowOperatorOverloading()) .SetValue("log", new Action(Console.WriteLine)) .SetValue("throws", new Func(Assert.Throws)) .SetValue("assert", new Action(Assert.True)) @@ -20,41 +20,41 @@ public MethodAmbiguityTests() .SetValue("TestClass", typeof(TestClass)) .SetValue("ChildTestClass", typeof(ChildTestClass)) ; - } - - void IDisposable.Dispose() - { - } - - private void RunTest(string source) - { - _engine.Execute(source); - } - - public class TestClass - { - public string this[int index] => "int"; - public string this[string index] => "string"; - public int TestMethod(double a, string b, double c) => 0; - public int TestMethod(double a, double b, double c) => 1; - public int TestMethod(TestClass a, string b, double c) => 2; - public int TestMethod(TestClass a, TestClass b, double c) => 3; - public int TestMethod(TestClass a, TestClass b, TestClass c) => 4; - public int TestMethod(TestClass a, double b, string c) => 5; - public int TestMethod(ChildTestClass a, double b, string c) => 6; - public int TestMethod(ChildTestClass a, string b, JsValue c) => 7; - - public static implicit operator TestClass(double i) => new TestClass(); - public static implicit operator double(TestClass tc) => 0; - public static explicit operator string(TestClass tc) => ""; - } - - public class ChildTestClass : TestClass { } - - [Fact] - public void BestMatchingMethodShouldBeCalled() - { - RunTest(@" + } + + void IDisposable.Dispose() + { + } + + private void RunTest(string source) + { + _engine.Execute(source); + } + + public class TestClass + { + public string this[int index] => "int"; + public string this[string index] => "string"; + public int TestMethod(double a, string b, double c) => 0; + public int TestMethod(double a, double b, double c) => 1; + public int TestMethod(TestClass a, string b, double c) => 2; + public int TestMethod(TestClass a, TestClass b, double c) => 3; + public int TestMethod(TestClass a, TestClass b, TestClass c) => 4; + public int TestMethod(TestClass a, double b, string c) => 5; + public int TestMethod(ChildTestClass a, double b, string c) => 6; + public int TestMethod(ChildTestClass a, string b, JsValue c) => 7; + + public static implicit operator TestClass(double i) => new TestClass(); + public static implicit operator double(TestClass tc) => 0; + public static explicit operator string(TestClass tc) => ""; + } + + public class ChildTestClass : TestClass { } + + [Fact] + public void BestMatchingMethodShouldBeCalled() + { + RunTest(@" var tc = new TestClass(); var cc = new ChildTestClass(); @@ -72,41 +72,41 @@ public void BestMatchingMethodShouldBeCalled() equal(6, tc.TestMethod(cc, 0, tc)); equal(7, tc.TestMethod(cc, '', {})); "); - } + } - [Fact] - public void IndexerCachesMethodsCorrectly() - { - RunTest(@" + [Fact] + public void IndexerCachesMethodsCorrectly() + { + RunTest(@" var tc = new TestClass(); equal('string:int', tc['Whistler'] + ':' + tc[10]); equal('int:string', tc[10] + ':' + tc['Whistler']); "); - } - - [Fact] - public void ShouldFavorOtherOverloadsOverObjectParameter() - { - var engine = new Engine(cfg => cfg.AllowOperatorOverloading()); - engine.SetValue("Class1", TypeReference.CreateTypeReference(engine)); - engine.SetValue("Class2", TypeReference.CreateTypeReference(engine)); - - Assert.Equal("Class1.Double[]", engine.Evaluate("Class1.Print([ 1, 2 ]);")); - Assert.Equal("Class1.ExpandoObject", engine.Evaluate("Class1.Print({ x: 1, y: 2 });")); - Assert.Equal("Class1.Int32", engine.Evaluate("Class1.Print(5);")); - Assert.Equal("Class2.Double[]", engine.Evaluate("Class2.Print([ 1, 2 ]); ")); - Assert.Equal("Class2.ExpandoObject", engine.Evaluate("Class2.Print({ x: 1, y: 2 });")); - Assert.Equal("Class2.Int32", engine.Evaluate("Class2.Print(5);")); - Assert.Equal("Class2.Object", engine.Evaluate("Class2.Print(() => '');")); - } - - [Fact] - public void ShouldMatchCorrectConstructors() - { - Engine engine = new Engine(); - engine.SetValue("MyClass", TypeReference.CreateTypeReference(engine, typeof(Class3))); - - engine.Execute(@" + } + + [Fact] + public void ShouldFavorOtherOverloadsOverObjectParameter() + { + var engine = new Engine(cfg => cfg.AllowOperatorOverloading()); + engine.SetValue("Class1", TypeReference.CreateTypeReference(engine)); + engine.SetValue("Class2", TypeReference.CreateTypeReference(engine)); + + Assert.Equal("Class1.Double[]", engine.Evaluate("Class1.Print([ 1, 2 ]);")); + Assert.Equal("Class1.ExpandoObject", engine.Evaluate("Class1.Print({ x: 1, y: 2 });")); + Assert.Equal("Class1.Int32", engine.Evaluate("Class1.Print(5);")); + Assert.Equal("Class2.Double[]", engine.Evaluate("Class2.Print([ 1, 2 ]); ")); + Assert.Equal("Class2.ExpandoObject", engine.Evaluate("Class2.Print({ x: 1, y: 2 });")); + Assert.Equal("Class2.Int32", engine.Evaluate("Class2.Print(5);")); + Assert.Equal("Class2.Object", engine.Evaluate("Class2.Print(() => '');")); + } + + [Fact] + public void ShouldMatchCorrectConstructors() + { + Engine engine = new Engine(); + engine.SetValue("MyClass", TypeReference.CreateTypeReference(engine, typeof(Class3))); + + engine.Execute(@" const a = new MyClass(); a.Print(1); // Works a.Print([1, 2]); // Works @@ -114,34 +114,33 @@ public void ShouldMatchCorrectConstructors() const b = new MyClass(1); // Works const c = new MyClass([1, 2]); // Throws 'an object must implement IConvertible' exception "); - } + } - private struct Class1 - { - public static string Print(ExpandoObject eo) => nameof(Class1) + "." + nameof(ExpandoObject); - public static string Print(double[] a) => nameof(Class1) + "." + nameof(Double) + "[]"; - public static string Print(int i) => nameof(Class1) + "." + nameof(Int32); - } + private struct Class1 + { + public static string Print(ExpandoObject eo) => nameof(Class1) + "." + nameof(ExpandoObject); + public static string Print(double[] a) => nameof(Class1) + "." + nameof(Double) + "[]"; + public static string Print(int i) => nameof(Class1) + "." + nameof(Int32); + } - private struct Class2 - { - public static string Print(ExpandoObject eo) => nameof(Class2) + "." + nameof(ExpandoObject); - public static string Print(double[] a) => nameof(Class2) + "." + nameof(Double) + "[]"; - public static string Print(int i) => nameof(Class2) + "." + nameof(Int32); - public static string Print(object o) => nameof(Class2) + "." + nameof(Object); - } + private struct Class2 + { + public static string Print(ExpandoObject eo) => nameof(Class2) + "." + nameof(ExpandoObject); + public static string Print(double[] a) => nameof(Class2) + "." + nameof(Double) + "[]"; + public static string Print(int i) => nameof(Class2) + "." + nameof(Int32); + public static string Print(object o) => nameof(Class2) + "." + nameof(Object); + } - public class Class3 - { - public Class3() { } + public class Class3 + { + public Class3() { } - public Class3(int a) => Console.WriteLine("Class1(int a): " + a); + public Class3(int a) => Console.WriteLine("Class1(int a): " + a); - public Class3(object a) => Console.WriteLine("Class1(object a): " + a); + public Class3(object a) => Console.WriteLine("Class1(object a): " + a); - public void Print(int a) => Console.WriteLine("Print(int a): " + a); + public void Print(int a) => Console.WriteLine("Print(int a): " + a); - public void Print(object a) => Console.WriteLine("Print(object a): " + a); - } + public void Print(object a) => Console.WriteLine("Print(object a): " + a); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/NullPropagation.cs b/Jint.Tests/Runtime/NullPropagation.cs index e84e3b36c6..1ac80ff835 100644 --- a/Jint.Tests/Runtime/NullPropagation.cs +++ b/Jint.Tests/Runtime/NullPropagation.cs @@ -2,69 +2,69 @@ using Jint.Runtime; using Jint.Runtime.Interop; -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class NullPropagation { - public class NullPropagation + public class NullPropagationReferenceResolver : IReferenceResolver { - public class NullPropagationReferenceResolver : IReferenceResolver + public bool TryUnresolvableReference(Engine engine, Reference reference, out JsValue value) { - public bool TryUnresolvableReference(Engine engine, Reference reference, out JsValue value) - { - value = reference.Base; - return true; - } + value = reference.Base; + return true; + } - public bool TryPropertyReference(Engine engine, Reference reference, ref JsValue value) - { - return value.IsNull() || value.IsUndefined(); - } + public bool TryPropertyReference(Engine engine, Reference reference, ref JsValue value) + { + return value.IsNull() || value.IsUndefined(); + } - public bool TryGetCallable(Engine engine, object callee, out JsValue value) + public bool TryGetCallable(Engine engine, object callee, out JsValue value) + { + if (callee is Reference reference) { - if (callee is Reference reference) + var name = reference.ReferencedName.AsString(); + if (name == "filter") { - var name = reference.ReferencedName.AsString(); - if (name == "filter") - { - value = new ClrFunction(engine, "map", (thisObj, values) => engine.Realm.Intrinsics.Array.ArrayCreate(0)); - return true; - } + value = new ClrFunction(engine, "map", (thisObj, values) => engine.Realm.Intrinsics.Array.ArrayCreate(0)); + return true; } - - value = new ClrFunction(engine, "anonymous", (thisObj, values) => thisObj); - return true; } - public bool CheckCoercible(JsValue value) - { - return true; - } + value = new ClrFunction(engine, "anonymous", (thisObj, values) => thisObj); + return true; } - [Fact] - public void CanCallFilterOnNull() + public bool CheckCoercible(JsValue value) { - var engine = new Engine(cfg => cfg.SetReferencesResolver(new NullPropagationReferenceResolver())); + return true; + } + } + + [Fact] + public void CanCallFilterOnNull() + { + var engine = new Engine(cfg => cfg.SetReferencesResolver(new NullPropagationReferenceResolver())); - const string Script = @" + const string Script = @" var input = {}; var output = { Tags : input.Tags.filter(x=>x!=null) }; "; - engine.Execute(Script); + engine.Execute(Script); - var output = engine.GetValue("output").AsObject(); + var output = engine.GetValue("output").AsObject(); - Assert.True(output.Get("Tags").IsArray()); - } + Assert.True(output.Get("Tags").IsArray()); + } - [Fact] - public void NullPropagationTest() - { - var engine = new Engine(cfg => cfg.SetReferencesResolver(new NullPropagationReferenceResolver())); + [Fact] + public void NullPropagationTest() + { + var engine = new Engine(cfg => cfg.SetReferencesResolver(new NullPropagationReferenceResolver())); - const string Script = @" + const string Script = @" var input = { Address : null }; @@ -79,28 +79,28 @@ public void NullPropagationTest() }; "; - engine.Execute(Script); + engine.Execute(Script); - var address = engine.GetValue("address"); - var city = engine.GetValue("city"); - var length = engine.GetValue("length"); - var output = engine.GetValue("output").AsObject(); + var address = engine.GetValue("address"); + var city = engine.GetValue("city"); + var length = engine.GetValue("length"); + var output = engine.GetValue("output").AsObject(); - Assert.Equal(JsValue.Null, address); - Assert.Equal(JsValue.Null, city); - Assert.Equal(JsValue.Null, length); + Assert.Equal(JsValue.Null, address); + Assert.Equal(JsValue.Null, city); + Assert.Equal(JsValue.Null, length); - Assert.Equal(JsValue.Null, output.Get("Count1")); - Assert.Equal(JsValue.Undefined, output.Get("Count2")); - } + Assert.Equal(JsValue.Null, output.Get("Count1")); + Assert.Equal(JsValue.Undefined, output.Get("Count2")); + } - [Fact] - public void NullPropagationFromArg() - { - var engine = new Engine(cfg => cfg.SetReferencesResolver(new NullPropagationReferenceResolver())); + [Fact] + public void NullPropagationFromArg() + { + var engine = new Engine(cfg => cfg.SetReferencesResolver(new NullPropagationReferenceResolver())); - const string Script = @" + const string Script = @" function test(arg) { return arg.Name; } @@ -109,39 +109,38 @@ function test2(arg) { return arg.Name.toUpperCase(); } "; - engine.Execute(Script); - var result = engine.Invoke("test", JsValue.Null); + engine.Execute(Script); + var result = engine.Invoke("test", JsValue.Null); - Assert.Equal(JsValue.Null, result); + Assert.Equal(JsValue.Null, result); - result = engine.Invoke("test2", JsValue.Null); + result = engine.Invoke("test2", JsValue.Null); - Assert.Equal(JsValue.Null, result); - } + Assert.Equal(JsValue.Null, result); + } - [Fact] - public void NullPropagationShouldNotAffectOperators() - { - var engine = new Engine(cfg => cfg.SetReferencesResolver(new NullPropagationReferenceResolver())); + [Fact] + public void NullPropagationShouldNotAffectOperators() + { + var engine = new Engine(cfg => cfg.SetReferencesResolver(new NullPropagationReferenceResolver())); - var jsObject = engine.Realm.Intrinsics.Object.Construct(Arguments.Empty); - jsObject.Set("NullField", JsValue.Null); + var jsObject = engine.Realm.Intrinsics.Object.Construct(Arguments.Empty); + jsObject.Set("NullField", JsValue.Null); - var script = @" + var script = @" this.is_nullfield_not_null = this.NullField !== null; this.is_notnullfield_not_null = this.NotNullField !== null; this.has_emptyfield_not_null = this.EmptyField !== null; "; - var wrapperScript = $@"function ExecutePatchScript(docInner){{ (function(doc){{ {script} }}).apply(docInner); }};"; + var wrapperScript = $@"function ExecutePatchScript(docInner){{ (function(doc){{ {script} }}).apply(docInner); }};"; - engine.Execute(wrapperScript, "main.js"); + engine.Execute(wrapperScript, "main.js"); - engine.Invoke("ExecutePatchScript", jsObject); + engine.Invoke("ExecutePatchScript", jsObject); - Assert.False(jsObject.Get("is_nullfield_not_null").AsBoolean()); - Assert.True(jsObject.Get("is_notnullfield_not_null").AsBoolean()); - Assert.True(jsObject.Get("has_emptyfield_not_null").AsBoolean()); - } + Assert.False(jsObject.Get("is_nullfield_not_null").AsBoolean()); + Assert.True(jsObject.Get("is_notnullfield_not_null").AsBoolean()); + Assert.True(jsObject.Get("has_emptyfield_not_null").AsBoolean()); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/NumberTests.cs b/Jint.Tests/Runtime/NumberTests.cs index 5b46fb6393..63e82052b9 100644 --- a/Jint.Tests/Runtime/NumberTests.cs +++ b/Jint.Tests/Runtime/NumberTests.cs @@ -1,148 +1,147 @@ using Jint.Runtime; -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class NumberTests { - public class NumberTests - { - private readonly Engine _engine; + private readonly Engine _engine; - public NumberTests() - { - _engine = new Engine() - .SetValue("log", new Action(Console.WriteLine)) - .SetValue("assert", new Action(Assert.True)) - .SetValue("equal", new Action(Assert.Equal)); - } + public NumberTests() + { + _engine = new Engine() + .SetValue("log", new Action(Console.WriteLine)) + .SetValue("assert", new Action(Assert.True)) + .SetValue("equal", new Action(Assert.Equal)); + } - private void RunTest(string source) - { - _engine.Execute(source); - } + private void RunTest(string source) + { + _engine.Execute(source); + } - [Theory] - [InlineData(1, "3.0e+0")] - [InlineData(50, "3.00000000000000000000000000000000000000000000000000e+0")] - [InlineData(100, "3.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e+0")] - public void ToExponential(int fractionDigits, string result) - { - var value = _engine.Evaluate($"(3).toExponential({fractionDigits}).toString()").AsString(); - Assert.Equal(result, value); - } + [Theory] + [InlineData(1, "3.0e+0")] + [InlineData(50, "3.00000000000000000000000000000000000000000000000000e+0")] + [InlineData(100, "3.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e+0")] + public void ToExponential(int fractionDigits, string result) + { + var value = _engine.Evaluate($"(3).toExponential({fractionDigits}).toString()").AsString(); + Assert.Equal(result, value); + } - [Theory] - [InlineData(1, "3.0")] - [InlineData(50, "3.00000000000000000000000000000000000000000000000000")] - [InlineData(99, "3.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")] - public void ToFixed(int fractionDigits, string result) - { - var value = _engine.Evaluate($"(3).toFixed({fractionDigits}).toString()").AsString(); - Assert.Equal(result, value); - } + [Theory] + [InlineData(1, "3.0")] + [InlineData(50, "3.00000000000000000000000000000000000000000000000000")] + [InlineData(99, "3.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")] + public void ToFixed(int fractionDigits, string result) + { + var value = _engine.Evaluate($"(3).toFixed({fractionDigits}).toString()").AsString(); + Assert.Equal(result, value); + } - [Fact] - public void ToFixedWith100FractionDigitsThrows() - { - var ex = Assert.Throws(() => _engine.Evaluate($"(3).toFixed(100)")); - Assert.Equal("100 fraction digits is not supported due to .NET format specifier limitation", ex.Message); - } + [Fact] + public void ToFixedWith100FractionDigitsThrows() + { + var ex = Assert.Throws(() => _engine.Evaluate($"(3).toFixed(100)")); + Assert.Equal("100 fraction digits is not supported due to .NET format specifier limitation", ex.Message); + } - [Theory] - [InlineData(1, "3")] - [InlineData(50, "3.0000000000000000000000000000000000000000000000000")] - [InlineData(100, "3.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")] - public void ToPrecision(int fractionDigits, string result) - { - var value = _engine.Evaluate($"(3).toPrecision({fractionDigits}).toString()").AsString(); - Assert.Equal(result, value); - } + [Theory] + [InlineData(1, "3")] + [InlineData(50, "3.0000000000000000000000000000000000000000000000000")] + [InlineData(100, "3.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")] + public void ToPrecision(int fractionDigits, string result) + { + var value = _engine.Evaluate($"(3).toPrecision({fractionDigits}).toString()").AsString(); + Assert.Equal(result, value); + } - [Theory] - [InlineData("1.7976931348623157e+308", double.MaxValue)] - public void ParseFloat(string input, double result) - { - var value = _engine.Evaluate($"parseFloat('{input}')").AsNumber(); - Assert.Equal(result, value); - } + [Theory] + [InlineData("1.7976931348623157e+308", double.MaxValue)] + public void ParseFloat(string input, double result) + { + var value = _engine.Evaluate($"parseFloat('{input}')").AsNumber(); + Assert.Equal(result, value); + } - // Results from node -v v18.18.0. - [Theory] - // Thousand separators. - [InlineData("1000000", "en-US", "1,000,000")] - [InlineData("1000000", "en-GB", "1,000,000")] - [InlineData("1000000", "de-DE", "1.000.000")] - // TODO. Fails in Win CI due to U+2009 - // Check https://learn.microsoft.com/en-us/dotnet/core/extensions/globalization-icu - // [InlineData("1000000", "fr-FR", "1 000 000")] - [InlineData("1000000", "es-ES", "1.000.000")] - [InlineData("1000000", "es-LA", "1.000.000")] - [InlineData("1000000", "es-MX", "1,000,000")] - [InlineData("1000000", "es-AR", "1.000.000")] - [InlineData("1000000", "es-CL", "1.000.000")] - // Comma separator. - [InlineData("1,23", "en-US", "23")] - [InlineData("1,23", "en-GB", "23")] - [InlineData("1,23", "de-DE", "23")] - [InlineData("1,23", "fr-FR", "23")] - [InlineData("1,23", "es-ES", "23")] - [InlineData("1,23", "es-LA", "23")] - [InlineData("1,23", "es-MX", "23")] - [InlineData("1,23", "es-AR", "23")] - [InlineData("1,23", "es-CL", "23")] - // Dot deicimal separator. - [InlineData("1.23", "en-US", "1.23")] - [InlineData("1.23", "en-GB", "1.23")] - [InlineData("1.23", "de-DE", "1,23")] - [InlineData("1.23", "fr-FR", "1,23")] - [InlineData("1.23", "es-ES", "1,23")] - [InlineData("1.23", "es-LA", "1,23")] - [InlineData("1.23", "es-MX", "1.23")] - [InlineData("1.23", "es-AR", "1,23")] - [InlineData("1.23", "es-CL", "1,23")] - // Scientific notation. - [InlineData("1e6", "en-US", "1,000,000")] - [InlineData("1e6", "en-GB", "1,000,000")] - [InlineData("1e6", "de-DE", "1.000.000")] - // TODO. Fails in Win CI due to U+2009 - // Check https://learn.microsoft.com/en-us/dotnet/core/extensions/globalization-icu - // [InlineData("1000000", "fr-FR", "1 000 000")] - [InlineData("1e6", "es-ES", "1.000.000")] - [InlineData("1e6", "es-LA", "1.000.000")] - [InlineData("1e6", "es-MX", "1,000,000")] - [InlineData("1e6", "es-AR", "1.000.000")] - [InlineData("1e6", "es-CL", "1.000.000")] - // Returns the correct max decimal degits for the respective cultures, rounded down. - [InlineData("1.234444449", "en-US", "1.234")] - [InlineData("1.234444449", "en-GB", "1.234")] - [InlineData("1.234444449", "de-DE", "1,234")] - [InlineData("1.234444449", "fr-FR", "1,234")] - [InlineData("1.234444449", "es-ES", "1,234")] - [InlineData("1.234444449", "es-LA", "1,234")] - [InlineData("1.234444449", "es-MX", "1.234")] - [InlineData("1.234444449", "es-AR", "1,234")] - [InlineData("1.234444449", "es-CL", "1,234")] - // Returns the correct max decimal degits for the respective cultures, rounded up. - [InlineData("1.234500001", "en-US", "1.235")] - [InlineData("1.234500001", "en-GB", "1.235")] - [InlineData("1.234500001", "de-DE", "1,235")] - [InlineData("1.234500001", "fr-FR", "1,235")] - [InlineData("1.234500001", "es-ES", "1,235")] - [InlineData("1.234500001", "es-LA", "1,235")] - [InlineData("1.234500001", "es-MX", "1.235")] - [InlineData("1.234500001", "es-AR", "1,235")] - [InlineData("1.234500001", "es-CL", "1,235")] - public void ToLocaleString(string parseNumber, string culture, string result) - { - var value = _engine.Evaluate($"({parseNumber}).toLocaleString('{culture}')").AsString(); - Assert.Equal(result, value); - } + // Results from node -v v18.18.0. + [Theory] + // Thousand separators. + [InlineData("1000000", "en-US", "1,000,000")] + [InlineData("1000000", "en-GB", "1,000,000")] + [InlineData("1000000", "de-DE", "1.000.000")] + // TODO. Fails in Win CI due to U+2009 + // Check https://learn.microsoft.com/en-us/dotnet/core/extensions/globalization-icu + // [InlineData("1000000", "fr-FR", "1 000 000")] + [InlineData("1000000", "es-ES", "1.000.000")] + [InlineData("1000000", "es-LA", "1.000.000")] + [InlineData("1000000", "es-MX", "1,000,000")] + [InlineData("1000000", "es-AR", "1.000.000")] + [InlineData("1000000", "es-CL", "1.000.000")] + // Comma separator. + [InlineData("1,23", "en-US", "23")] + [InlineData("1,23", "en-GB", "23")] + [InlineData("1,23", "de-DE", "23")] + [InlineData("1,23", "fr-FR", "23")] + [InlineData("1,23", "es-ES", "23")] + [InlineData("1,23", "es-LA", "23")] + [InlineData("1,23", "es-MX", "23")] + [InlineData("1,23", "es-AR", "23")] + [InlineData("1,23", "es-CL", "23")] + // Dot deicimal separator. + [InlineData("1.23", "en-US", "1.23")] + [InlineData("1.23", "en-GB", "1.23")] + [InlineData("1.23", "de-DE", "1,23")] + [InlineData("1.23", "fr-FR", "1,23")] + [InlineData("1.23", "es-ES", "1,23")] + [InlineData("1.23", "es-LA", "1,23")] + [InlineData("1.23", "es-MX", "1.23")] + [InlineData("1.23", "es-AR", "1,23")] + [InlineData("1.23", "es-CL", "1,23")] + // Scientific notation. + [InlineData("1e6", "en-US", "1,000,000")] + [InlineData("1e6", "en-GB", "1,000,000")] + [InlineData("1e6", "de-DE", "1.000.000")] + // TODO. Fails in Win CI due to U+2009 + // Check https://learn.microsoft.com/en-us/dotnet/core/extensions/globalization-icu + // [InlineData("1000000", "fr-FR", "1 000 000")] + [InlineData("1e6", "es-ES", "1.000.000")] + [InlineData("1e6", "es-LA", "1.000.000")] + [InlineData("1e6", "es-MX", "1,000,000")] + [InlineData("1e6", "es-AR", "1.000.000")] + [InlineData("1e6", "es-CL", "1.000.000")] + // Returns the correct max decimal degits for the respective cultures, rounded down. + [InlineData("1.234444449", "en-US", "1.234")] + [InlineData("1.234444449", "en-GB", "1.234")] + [InlineData("1.234444449", "de-DE", "1,234")] + [InlineData("1.234444449", "fr-FR", "1,234")] + [InlineData("1.234444449", "es-ES", "1,234")] + [InlineData("1.234444449", "es-LA", "1,234")] + [InlineData("1.234444449", "es-MX", "1.234")] + [InlineData("1.234444449", "es-AR", "1,234")] + [InlineData("1.234444449", "es-CL", "1,234")] + // Returns the correct max decimal degits for the respective cultures, rounded up. + [InlineData("1.234500001", "en-US", "1.235")] + [InlineData("1.234500001", "en-GB", "1.235")] + [InlineData("1.234500001", "de-DE", "1,235")] + [InlineData("1.234500001", "fr-FR", "1,235")] + [InlineData("1.234500001", "es-ES", "1,235")] + [InlineData("1.234500001", "es-LA", "1,235")] + [InlineData("1.234500001", "es-MX", "1.235")] + [InlineData("1.234500001", "es-AR", "1,235")] + [InlineData("1.234500001", "es-CL", "1,235")] + public void ToLocaleString(string parseNumber, string culture, string result) + { + var value = _engine.Evaluate($"({parseNumber}).toLocaleString('{culture}')").AsString(); + Assert.Equal(result, value); + } - [Theory] - // Does not add extra zeros of there is no cuture argument. - [InlineData("123456")] - public void ToLocaleStringNoArg(string parseNumber) - { - var value = _engine.Evaluate($"({parseNumber}).toLocaleString()").AsString(); - Assert.DoesNotContain(".0", value); - } + [Theory] + // Does not add extra zeros of there is no cuture argument. + [InlineData("123456")] + public void ToLocaleStringNoArg(string parseNumber) + { + var value = _engine.Evaluate($"({parseNumber}).toLocaleString()").AsString(); + Assert.DoesNotContain(".0", value); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/ObjectInstanceTests.cs b/Jint.Tests/Runtime/ObjectInstanceTests.cs index 6deaa18bb2..de97d67570 100644 --- a/Jint.Tests/Runtime/ObjectInstanceTests.cs +++ b/Jint.Tests/Runtime/ObjectInstanceTests.cs @@ -1,32 +1,32 @@ using Jint.Native; -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class ObjectInstanceTests { - public class ObjectInstanceTests + [Fact] + public void RemovingFirstPropertyFromObjectInstancePropertiesBucketAndEnumerating() { - [Fact] - public void RemovingFirstPropertyFromObjectInstancePropertiesBucketAndEnumerating() - { - var engine = new Engine(); - var instance = new JsObject(engine); - instance.FastSetDataProperty("bare", JsValue.Null); - instance.FastSetDataProperty("scope", JsValue.Null); - instance.RemoveOwnProperty("bare"); - var propertyNames = instance.GetOwnProperties().Select(x => x.Key).ToList(); - Assert.Equal(new JsValue[] { "scope" }, propertyNames); - } + var engine = new Engine(); + var instance = new JsObject(engine); + instance.FastSetDataProperty("bare", JsValue.Null); + instance.FastSetDataProperty("scope", JsValue.Null); + instance.RemoveOwnProperty("bare"); + var propertyNames = instance.GetOwnProperties().Select(x => x.Key).ToList(); + Assert.Equal(new JsValue[] { "scope" }, propertyNames); + } - [Theory] - [InlineData("Array")] - [InlineData("Boolean")] - [InlineData("Date")] - [InlineData("Error")] - [InlineData("Number")] - [InlineData("Object")] - [InlineData("String")] - public void ExtendingObjects(string baseType) - { - var code = $@" + [Theory] + [InlineData("Array")] + [InlineData("Boolean")] + [InlineData("Date")] + [InlineData("Error")] + [InlineData("Number")] + [InlineData("Object")] + [InlineData("String")] + public void ExtendingObjects(string baseType) + { + var code = $@" class My{baseType} extends {baseType} {{ constructor(...args) {{ super(...args); @@ -35,17 +35,16 @@ class My{baseType} extends {baseType} {{ const o = new My{baseType}(); o.constructor === My{baseType}"; - var engine = new Engine(); - Assert.True(engine.Evaluate(code).AsBoolean()); - } + var engine = new Engine(); + Assert.True(engine.Evaluate(code).AsBoolean()); + } - [Fact] - public void ShouldHavePrototypeInPlaceByDefault() - { - var engine = new Engine(); - var instance = new JsObject(engine); - Assert.NotNull(instance.GetPrototypeOf()); - Assert.Equal("[object Object]", instance.ToString()); - } + [Fact] + public void ShouldHavePrototypeInPlaceByDefault() + { + var engine = new Engine(); + var instance = new JsObject(engine); + Assert.NotNull(instance.GetPrototypeOf()); + Assert.Equal("[object Object]", instance.ToString()); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/OperatorOverloadingTests.cs b/Jint.Tests/Runtime/OperatorOverloadingTests.cs index 19960ce4d4..dcfb9aeeb9 100644 --- a/Jint.Tests/Runtime/OperatorOverloadingTests.cs +++ b/Jint.Tests/Runtime/OperatorOverloadingTests.cs @@ -1,126 +1,126 @@ using Jint.Native.String; using Jint.Runtime.Interop; -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class OperatorOverloadingTests { - public class OperatorOverloadingTests - { - private readonly Engine _engine; + private readonly Engine _engine; - public OperatorOverloadingTests() - { - _engine = new Engine(cfg => cfg + public OperatorOverloadingTests() + { + _engine = new Engine(cfg => cfg .AllowOperatorOverloading()) - .SetValue("log", new Action(Console.WriteLine)) - .SetValue("assert", new Action(Assert.True)) - .SetValue("assertFalse", new Action(Assert.False)) - .SetValue("equal", new Action(Assert.Equal)) - .SetValue("Vector2", typeof(Vector2)) - .SetValue("Vector3", typeof(Vector3)) - .SetValue("Vector2Child", typeof(Vector2Child)); - } + .SetValue("log", new Action(Console.WriteLine)) + .SetValue("assert", new Action(Assert.True)) + .SetValue("assertFalse", new Action(Assert.False)) + .SetValue("equal", new Action(Assert.Equal)) + .SetValue("Vector2", typeof(Vector2)) + .SetValue("Vector3", typeof(Vector3)) + .SetValue("Vector2Child", typeof(Vector2Child)); + } - private void RunTest(string source) - { - _engine.Execute(source); - } + private void RunTest(string source) + { + _engine.Execute(source); + } + + public class Vector2 + { + public double X { get; } + public double Y { get; } + public double SqrMagnitude => X * X + Y * Y; + public double Magnitude => Math.Sqrt(SqrMagnitude); - public class Vector2 + public Vector2(double x, double y) { - public double X { get; } - public double Y { get; } - public double SqrMagnitude => X * X + Y * Y; - public double Magnitude => Math.Sqrt(SqrMagnitude); - - public Vector2(double x, double y) - { - X = x; - Y = y; - } - - public static Vector2 operator +(Vector2 left, Vector2 right) => new Vector2(left.X + right.X, left.Y + right.Y); - public static Vector2 operator +(Vector2 left, double right) => new Vector2(left.X + right, left.Y + right); - public static Vector2 operator +(string left, Vector2 right) => new Vector2(right.X, right.Y); - public static Vector2 operator +(double left, Vector2 right) => new Vector2(right.X + left, right.Y + left); - public static Vector2 operator -(Vector2 left, Vector2 right) => new Vector2(left.X - right.X, left.Y - right.Y); - public static Vector2 operator *(Vector2 left, double right) => new Vector2(left.X * right, left.Y * right); - public static Vector2 operator /(Vector2 left, double right) => new Vector2(left.X / right, left.Y / right); - - public static bool operator >(Vector2 left, Vector2 right) => left.Magnitude > right.Magnitude; - public static bool operator <(Vector2 left, Vector2 right) => left.Magnitude < right.Magnitude; - public static bool operator >=(Vector2 left, Vector2 right) => left.Magnitude >= right.Magnitude; - public static bool operator <=(Vector2 left, Vector2 right) => left.Magnitude <= right.Magnitude; - public static Vector2 operator %(Vector2 left, Vector2 right) => new Vector2(left.X % right.X, left.Y % right.Y); - public static double operator &(Vector2 left, Vector2 right) => left.X * right.X + left.Y * right.Y; - public static Vector2 operator |(Vector2 left, Vector2 right) => right * ((left & right) / right.SqrMagnitude); - - - public static double operator +(Vector2 operand) => operand.Magnitude; - public static Vector2 operator -(Vector2 operand) => new Vector2(-operand.X, -operand.Y); - public static bool operator !(Vector2 operand) => operand.Magnitude == 0; - public static Vector2 operator ~(Vector2 operand) => new Vector2(operand.Y, operand.X); - public static Vector2 operator ++(Vector2 operand) => new Vector2(operand.X + 1, operand.Y + 1); - public static Vector2 operator --(Vector2 operand) => new Vector2(operand.X - 1, operand.Y - 1); - - public static implicit operator Vector3(Vector2 val) => new Vector3(val.X, val.Y, 0); - public static bool operator !=(Vector2 left, Vector2 right) => !(left == right); - public static bool operator ==(Vector2 left, Vector2 right) => left.X == right.X && left.Y == right.Y; - public override bool Equals(object obj) => ReferenceEquals(this, obj); - public override int GetHashCode() => X.GetHashCode() + Y.GetHashCode(); + X = x; + Y = y; } - public class Vector2Child : Vector2 - { - public Vector2Child(double x, double y) : base(x, y) { } + public static Vector2 operator +(Vector2 left, Vector2 right) => new Vector2(left.X + right.X, left.Y + right.Y); + public static Vector2 operator +(Vector2 left, double right) => new Vector2(left.X + right, left.Y + right); + public static Vector2 operator +(string left, Vector2 right) => new Vector2(right.X, right.Y); + public static Vector2 operator +(double left, Vector2 right) => new Vector2(right.X + left, right.Y + left); + public static Vector2 operator -(Vector2 left, Vector2 right) => new Vector2(left.X - right.X, left.Y - right.Y); + public static Vector2 operator *(Vector2 left, double right) => new Vector2(left.X * right, left.Y * right); + public static Vector2 operator /(Vector2 left, double right) => new Vector2(left.X / right, left.Y / right); + + public static bool operator >(Vector2 left, Vector2 right) => left.Magnitude > right.Magnitude; + public static bool operator <(Vector2 left, Vector2 right) => left.Magnitude < right.Magnitude; + public static bool operator >=(Vector2 left, Vector2 right) => left.Magnitude >= right.Magnitude; + public static bool operator <=(Vector2 left, Vector2 right) => left.Magnitude <= right.Magnitude; + public static Vector2 operator %(Vector2 left, Vector2 right) => new Vector2(left.X % right.X, left.Y % right.Y); + public static double operator &(Vector2 left, Vector2 right) => left.X * right.X + left.Y * right.Y; + public static Vector2 operator |(Vector2 left, Vector2 right) => right * ((left & right) / right.SqrMagnitude); + + + public static double operator +(Vector2 operand) => operand.Magnitude; + public static Vector2 operator -(Vector2 operand) => new Vector2(-operand.X, -operand.Y); + public static bool operator !(Vector2 operand) => operand.Magnitude == 0; + public static Vector2 operator ~(Vector2 operand) => new Vector2(operand.Y, operand.X); + public static Vector2 operator ++(Vector2 operand) => new Vector2(operand.X + 1, operand.Y + 1); + public static Vector2 operator --(Vector2 operand) => new Vector2(operand.X - 1, operand.Y - 1); + + public static implicit operator Vector3(Vector2 val) => new Vector3(val.X, val.Y, 0); + public static bool operator !=(Vector2 left, Vector2 right) => !(left == right); + public static bool operator ==(Vector2 left, Vector2 right) => left.X == right.X && left.Y == right.Y; + public override bool Equals(object obj) => ReferenceEquals(this, obj); + public override int GetHashCode() => X.GetHashCode() + Y.GetHashCode(); + } - public static Vector2Child operator +(Vector2Child left, double right) => new Vector2Child(left.X + 2 * right, left.Y + 2 * right); - } + public class Vector2Child : Vector2 + { + public Vector2Child(double x, double y) : base(x, y) { } - public class Vector3 + public static Vector2Child operator +(Vector2Child left, double right) => new Vector2Child(left.X + 2 * right, left.Y + 2 * right); + } + + public class Vector3 + { + public double X { get; } + public double Y { get; } + public double Z { get; } + + public Vector3(double x, double y, double z) { - public double X { get; } - public double Y { get; } - public double Z { get; } - - public Vector3(double x, double y, double z) - { - X = x; - Y = y; - Z = z; - } - - public static Vector3 operator +(Vector3 left, double right) => new Vector3(left.X + right, left.Y + right, left.Z + right); - public static Vector3 operator +(double left, Vector3 right) => new Vector3(right.X + left, right.Y + left, right.Z + left); - public static Vector3 operator +(Vector3 left, Vector3 right) => new Vector3(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + X = x; + Y = y; + Z = z; } - private struct Vector2D - { - public double X { get; set; } - public double Y { get; set; } + public static Vector3 operator +(Vector3 left, double right) => new Vector3(left.X + right, left.Y + right, left.Z + right); + public static Vector3 operator +(double left, Vector3 right) => new Vector3(right.X + left, right.Y + left, right.Z + left); + public static Vector3 operator +(Vector3 left, Vector3 right) => new Vector3(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + } - public Vector2D(double x, double y) - { - X = x; - Y = y; - } + private struct Vector2D + { + public double X { get; set; } + public double Y { get; set; } + public Vector2D(double x, double y) + { + X = x; + Y = y; + } - public static Vector2D operator +(Vector2D lhs, Vector2D rhs) - { - return new Vector2D(lhs.X + rhs.X, lhs.Y + rhs.Y); - } - public override string ToString() - { - return $"({X}, {Y})"; - } + public static Vector2D operator +(Vector2D lhs, Vector2D rhs) + { + return new Vector2D(lhs.X + rhs.X, lhs.Y + rhs.Y); } - [Fact] - public void OperatorOverloading_BinaryOperators() + public override string ToString() { - RunTest(@" + return $"({X}, {Y})"; + } + } + + [Fact] + public void OperatorOverloading_BinaryOperators() + { + RunTest(@" var v1 = new Vector2(1, 2); var v2 = new Vector2(3, 4); var n = 6; @@ -179,12 +179,12 @@ public void OperatorOverloading_BinaryOperators() assertFalse(vBig < vSmall); assertFalse(vBig <= vSmall); "); - } + } - [Fact] - public void OperatorOverloading_AssignmentOperators() - { - RunTest(@" + [Fact] + public void OperatorOverloading_AssignmentOperators() + { + RunTest(@" var v1 = new Vector2(1, 2); var v2 = new Vector2(3, 4); var n = 6; @@ -233,12 +233,12 @@ public void OperatorOverloading_AssignmentOperators() equal(2, r9.X); equal(2, r9.Y); "); - } + } - [Fact] - public void OperatorOverloading_ShouldCoerceTypes() - { - RunTest(@" + [Fact] + public void OperatorOverloading_ShouldCoerceTypes() + { + RunTest(@" var v1 = new Vector2(1, 2); var v2 = new Vector3(4, 5, 6); var res = v1 + v2; @@ -246,12 +246,12 @@ public void OperatorOverloading_ShouldCoerceTypes() equal(7, res.Y); equal(6, res.Z); "); - } + } - [Fact] - public void OperatorOverloading_ShouldWorkForEqualityButNotForStrictEquality() - { - RunTest(@" + [Fact] + public void OperatorOverloading_ShouldWorkForEqualityButNotForStrictEquality() + { + RunTest(@" var v1 = new Vector2(1, 2); var v2 = new Vector2(1, 2); assert(v1 == v2); @@ -264,12 +264,12 @@ public void OperatorOverloading_ShouldWorkForEqualityButNotForStrictEquality() var z2 = new Vector3(1, 2, 3); assertFalse(z1 == z2); "); - } + } - [Fact] - public void OperatorOverloading_UnaryOperators() - { - RunTest(@" + [Fact] + public void OperatorOverloading_UnaryOperators() + { + RunTest(@" var v0 = new Vector2(0, 0); var v = new Vector2(3, 4); var rv = -v; @@ -287,12 +287,12 @@ public void OperatorOverloading_UnaryOperators() equal(4, bv.X); equal(3, bv.Y); "); - } + } - [Fact] - public void OperatorOverloading_IncrementOperatorShouldWork() - { - RunTest(@" + [Fact] + public void OperatorOverloading_IncrementOperatorShouldWork() + { + RunTest(@" var v = new Vector2(3, 22); var original = v; var pre = ++v; @@ -310,12 +310,12 @@ public void OperatorOverloading_IncrementOperatorShouldWork() equal(4, decPost.X); equal(3, v.X); "); - } + } - [Fact] - public void OperatorOverloading_ShouldWorkOnDerivedClasses() - { - RunTest(@" + [Fact] + public void OperatorOverloading_ShouldWorkOnDerivedClasses() + { + RunTest(@" var v1 = new Vector2Child(1, 2); var v2 = new Vector2Child(3, 4); var n = 5; @@ -331,12 +331,12 @@ public void OperatorOverloading_ShouldWorkOnDerivedClasses() equal(11, v1n.X); equal(12, v1n.Y); "); - } + } - [Fact] - public void OperatorOverloading_ShouldEvaluateOnlyOnce() - { - RunTest(@" + [Fact] + public void OperatorOverloading_ShouldEvaluateOnlyOnce() + { + RunTest(@" var c; var resolve = v => { c++; return v; }; @@ -355,18 +355,17 @@ public void OperatorOverloading_ShouldEvaluateOnlyOnce() equal(n3, -1); equal(c, 1); "); - } + } - [Fact] - public void ShouldAllowStringConcatenateForOverloaded() - { - var engine = new Engine(cfg => cfg.AllowOperatorOverloading()); - engine.SetValue("Vector2D", TypeReference.CreateTypeReference(engine)); - engine.SetValue("log", new Action(Console.WriteLine)); + [Fact] + public void ShouldAllowStringConcatenateForOverloaded() + { + var engine = new Engine(cfg => cfg.AllowOperatorOverloading()); + engine.SetValue("Vector2D", TypeReference.CreateTypeReference(engine)); + engine.SetValue("log", new Action(Console.WriteLine)); - engine.Evaluate("let v1 = new Vector2D(1, 2);"); - Assert.Equal("(1, 2)", engine.Evaluate("new String(v1)").As().StringData.ToString()); - Assert.Equal("### (1, 2) ###", engine.Evaluate("'### ' + v1 + ' ###'")); - } + engine.Evaluate("let v1 = new Vector2D(1, 2);"); + Assert.Equal("(1, 2)", engine.Evaluate("new String(v1)").As().StringData.ToString()); + Assert.Equal("### (1, 2) ###", engine.Evaluate("'### ' + v1 + ' ###'")); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/SamplesTests.cs b/Jint.Tests/Runtime/SamplesTests.cs index d14d26d77c..c1d5580fcf 100644 --- a/Jint.Tests/Runtime/SamplesTests.cs +++ b/Jint.Tests/Runtime/SamplesTests.cs @@ -1,35 +1,34 @@ -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class SamplesTests : IDisposable { - public class SamplesTests : IDisposable - { - private readonly Engine _engine; + private readonly Engine _engine; - public SamplesTests() - { - _engine = new Engine() + public SamplesTests() + { + _engine = new Engine() .SetValue("log", new Action(Console.WriteLine)) .SetValue("assert", new Action(Assert.True)) - ; - } + ; + } - void IDisposable.Dispose() - { - } + void IDisposable.Dispose() + { + } - private void RunTest(string source) - { - _engine.Execute(source); - } + private void RunTest(string source) + { + _engine.Execute(source); + } - [Fact] - public void GithubReadme1() - { - var square = new Engine() - .SetValue("x", 3) - .Evaluate("x * x") - .ToObject(); + [Fact] + public void GithubReadme1() + { + var square = new Engine() + .SetValue("x", 3) + .Evaluate("x * x") + .ToObject(); - Assert.Equal(9d, square); - } + Assert.Equal(9d, square); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/StringTetsLithuaniaData.cs b/Jint.Tests/Runtime/StringTetsLithuaniaData.cs index 5e21c0f92a..319457a2aa 100644 --- a/Jint.Tests/Runtime/StringTetsLithuaniaData.cs +++ b/Jint.Tests/Runtime/StringTetsLithuaniaData.cs @@ -1,97 +1,96 @@ -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class StringTetsLithuaniaData { - public class StringTetsLithuaniaData - { - // Contains the non-uppercased string that will be processed by the engine and the expected result. - private readonly TheoryData fullSetOfData = new TheoryData(); - // From: https://github.com/tc39/test262/blob/main/test/intl402/String/prototype/toLocaleUpperCase/special_casing_Lithuanian.js - private readonly string[] softDotted = [ - "\u0069", "\u006A", // LATIN SMALL LETTER I..LATIN SMALL LETTER J - "\u012F", // LATIN SMALL LETTER I WITH OGONEK - "\u0249", // LATIN SMALL LETTER J WITH STROKE - "\u0268", // LATIN SMALL LETTER I WITH STROKE - "\u029D", // LATIN SMALL LETTER J WITH CROSSED-TAIL - "\u02B2", // MODIFIER LETTER SMALL J - "\u03F3", // GREEK LETTER YOT - "\u0456", // CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I - "\u0458", // CYRILLIC SMALL LETTER JE - "\u1D62", // LATIN SUBSCRIPT SMALL LETTER I - "\u1D96", // LATIN SMALL LETTER I WITH RETROFLEX HOOK - "\u1DA4", // MODIFIER LETTER SMALL I WITH STROKE - "\u1DA8", // MODIFIER LETTER SMALL J WITH CROSSED-TAIL - "\u1E2D", // LATIN SMALL LETTER I WITH TILDE BELOW - "\u1ECB", // LATIN SMALL LETTER I WITH DOT BELOW - "\u2071", // SUPERSCRIPT LATIN SMALL LETTER I - "\u2148", "\u2149", // DOUBLE-STRUCK ITALIC SMALL I..DOUBLE-STRUCK ITALIC SMALL J - "\u2C7C", // LATIN SUBSCRIPT SMALL LETTER J - "\uD835\uDC22", "\uD835\uDC23", // MATHEMATICAL BOLD SMALL I..MATHEMATICAL BOLD SMALL J - "\uD835\uDC56", "\uD835\uDC57", // MATHEMATICAL ITALIC SMALL I..MATHEMATICAL ITALIC SMALL J - "\uD835\uDC8A", "\uD835\uDC8B", // MATHEMATICAL BOLD ITALIC SMALL I..MATHEMATICAL BOLD ITALIC SMALL J - "\uD835\uDCBE", "\uD835\uDCBF", // MATHEMATICAL SCRIPT SMALL I..MATHEMATICAL SCRIPT SMALL J - "\uD835\uDCF2", "\uD835\uDCF3", // MATHEMATICAL BOLD SCRIPT SMALL I..MATHEMATICAL BOLD SCRIPT SMALL J - "\uD835\uDD26", "\uD835\uDD27", // MATHEMATICAL FRAKTUR SMALL I..MATHEMATICAL FRAKTUR SMALL J - "\uD835\uDD5A", "\uD835\uDD5B", // MATHEMATICAL DOUBLE-STRUCK SMALL I..MATHEMATICAL DOUBLE-STRUCK SMALL J - "\uD835\uDD8E", "\uD835\uDD8F", // MATHEMATICAL BOLD FRAKTUR SMALL I..MATHEMATICAL BOLD FRAKTUR SMALL J - "\uD835\uDDC2", "\uD835\uDDC3", // MATHEMATICAL SANS-SERIF SMALL I..MATHEMATICAL SANS-SERIF SMALL J - "\uD835\uDDF6", "\uD835\uDDF7", // MATHEMATICAL SANS-SERIF BOLD SMALL I..MATHEMATICAL SANS-SERIF BOLD SMALL J - "\uD835\uDE2A", "\uD835\uDE2B", // MATHEMATICAL SANS-SERIF ITALIC SMALL I..MATHEMATICAL SANS-SERIF ITALIC SMALL J - "\uD835\uDE5E", "\uD835\uDE5F", // MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL I..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL J - "\uD835\uDE92", "\uD835\uDE93", // MATHEMATICAL MONOSPACE SMALL I..MATHEMATICAL MONOSPACE SMALL J - ]; + // Contains the non-uppercased string that will be processed by the engine and the expected result. + private readonly TheoryData fullSetOfData = new TheoryData(); + // From: https://github.com/tc39/test262/blob/main/test/intl402/String/prototype/toLocaleUpperCase/special_casing_Lithuanian.js + private readonly string[] softDotted = [ + "\u0069", "\u006A", // LATIN SMALL LETTER I..LATIN SMALL LETTER J + "\u012F", // LATIN SMALL LETTER I WITH OGONEK + "\u0249", // LATIN SMALL LETTER J WITH STROKE + "\u0268", // LATIN SMALL LETTER I WITH STROKE + "\u029D", // LATIN SMALL LETTER J WITH CROSSED-TAIL + "\u02B2", // MODIFIER LETTER SMALL J + "\u03F3", // GREEK LETTER YOT + "\u0456", // CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I + "\u0458", // CYRILLIC SMALL LETTER JE + "\u1D62", // LATIN SUBSCRIPT SMALL LETTER I + "\u1D96", // LATIN SMALL LETTER I WITH RETROFLEX HOOK + "\u1DA4", // MODIFIER LETTER SMALL I WITH STROKE + "\u1DA8", // MODIFIER LETTER SMALL J WITH CROSSED-TAIL + "\u1E2D", // LATIN SMALL LETTER I WITH TILDE BELOW + "\u1ECB", // LATIN SMALL LETTER I WITH DOT BELOW + "\u2071", // SUPERSCRIPT LATIN SMALL LETTER I + "\u2148", "\u2149", // DOUBLE-STRUCK ITALIC SMALL I..DOUBLE-STRUCK ITALIC SMALL J + "\u2C7C", // LATIN SUBSCRIPT SMALL LETTER J + "\uD835\uDC22", "\uD835\uDC23", // MATHEMATICAL BOLD SMALL I..MATHEMATICAL BOLD SMALL J + "\uD835\uDC56", "\uD835\uDC57", // MATHEMATICAL ITALIC SMALL I..MATHEMATICAL ITALIC SMALL J + "\uD835\uDC8A", "\uD835\uDC8B", // MATHEMATICAL BOLD ITALIC SMALL I..MATHEMATICAL BOLD ITALIC SMALL J + "\uD835\uDCBE", "\uD835\uDCBF", // MATHEMATICAL SCRIPT SMALL I..MATHEMATICAL SCRIPT SMALL J + "\uD835\uDCF2", "\uD835\uDCF3", // MATHEMATICAL BOLD SCRIPT SMALL I..MATHEMATICAL BOLD SCRIPT SMALL J + "\uD835\uDD26", "\uD835\uDD27", // MATHEMATICAL FRAKTUR SMALL I..MATHEMATICAL FRAKTUR SMALL J + "\uD835\uDD5A", "\uD835\uDD5B", // MATHEMATICAL DOUBLE-STRUCK SMALL I..MATHEMATICAL DOUBLE-STRUCK SMALL J + "\uD835\uDD8E", "\uD835\uDD8F", // MATHEMATICAL BOLD FRAKTUR SMALL I..MATHEMATICAL BOLD FRAKTUR SMALL J + "\uD835\uDDC2", "\uD835\uDDC3", // MATHEMATICAL SANS-SERIF SMALL I..MATHEMATICAL SANS-SERIF SMALL J + "\uD835\uDDF6", "\uD835\uDDF7", // MATHEMATICAL SANS-SERIF BOLD SMALL I..MATHEMATICAL SANS-SERIF BOLD SMALL J + "\uD835\uDE2A", "\uD835\uDE2B", // MATHEMATICAL SANS-SERIF ITALIC SMALL I..MATHEMATICAL SANS-SERIF ITALIC SMALL J + "\uD835\uDE5E", "\uD835\uDE5F", // MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL I..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL J + "\uD835\uDE92", "\uD835\uDE93", // MATHEMATICAL MONOSPACE SMALL I..MATHEMATICAL MONOSPACE SMALL J + ]; - // Results obtained from node -v 18.12.0. - private readonly string[] softDottedUpperCased = [ - "I", "J", "Į", "Ɉ", "Ɨ", "Ʝ", "ʲ", "Ϳ", "І", "Ј", - "ᵢ", "ᶖ", "ᶤ", "ᶨ", "Ḭ", "Ị", "ⁱ", "ⅈ", "ⅉ", "ⱼ", - "𝐢", "𝐣", "𝑖", "𝑗", "𝒊", "𝒋", "𝒾", "𝒿", "𝓲", "𝓳", - "𝔦", "𝔧", "𝕚", "𝕛", "𝖎", "𝖏", "𝗂", "𝗃", "𝗶", "𝗷", - "𝘪", "𝘫", "𝙞", "𝙟", "𝚒", "𝚓", - ]; + // Results obtained from node -v 18.12.0. + private readonly string[] softDottedUpperCased = [ + "I", "J", "Į", "Ɉ", "Ɨ", "Ʝ", "ʲ", "Ϳ", "І", "Ј", + "ᵢ", "ᶖ", "ᶤ", "ᶨ", "Ḭ", "Ị", "ⁱ", "ⅈ", "ⅉ", "ⱼ", + "𝐢", "𝐣", "𝑖", "𝑗", "𝒊", "𝒋", "𝒾", "𝒿", "𝓲", "𝓳", + "𝔦", "𝔧", "𝕚", "𝕛", "𝖎", "𝖏", "𝗂", "𝗃", "𝗶", "𝗷", + "𝘪", "𝘫", "𝙞", "𝙟", "𝚒", "𝚓", + ]; - /// - /// Creates and adds the data to that will be used for the tests. Six cases: - /// 1.- String with character at the beginning of the string. - /// 2.- String with double character at the beginning of the string. - /// 3.- String with character at the middle of the string. - /// 4.- String with double character at the middle of the string. - /// 5.- String with character at the end of the string. - /// 6.- String with double character at the end of the string. - /// - private void AddStringsForChars(string nonCapChar, string toUpperChar) - { - fullSetOfData.Add($"{nonCapChar}lorem ipsum", $"{toUpperChar}LOREM IPSUM"); - fullSetOfData.Add($"{nonCapChar}{nonCapChar}lorem ipsum", $"{toUpperChar}{toUpperChar}LOREM IPSUM"); - fullSetOfData.Add($"lorem{nonCapChar}ipsum", $"LOREM{toUpperChar}IPSUM"); - fullSetOfData.Add($"lorem{nonCapChar}{nonCapChar}ipsum", $"LOREM{toUpperChar}{toUpperChar}IPSUM"); - fullSetOfData.Add($"lorem ipsum{nonCapChar}", $"LOREM IPSUM{toUpperChar}"); - fullSetOfData.Add($"lorem ipsum{nonCapChar}{nonCapChar}", $"LOREM IPSUM{toUpperChar}{toUpperChar}"); - } + /// + /// Creates and adds the data to that will be used for the tests. Six cases: + /// 1.- String with character at the beginning of the string. + /// 2.- String with double character at the beginning of the string. + /// 3.- String with character at the middle of the string. + /// 4.- String with double character at the middle of the string. + /// 5.- String with character at the end of the string. + /// 6.- String with double character at the end of the string. + /// + private void AddStringsForChars(string nonCapChar, string toUpperChar) + { + fullSetOfData.Add($"{nonCapChar}lorem ipsum", $"{toUpperChar}LOREM IPSUM"); + fullSetOfData.Add($"{nonCapChar}{nonCapChar}lorem ipsum", $"{toUpperChar}{toUpperChar}LOREM IPSUM"); + fullSetOfData.Add($"lorem{nonCapChar}ipsum", $"LOREM{toUpperChar}IPSUM"); + fullSetOfData.Add($"lorem{nonCapChar}{nonCapChar}ipsum", $"LOREM{toUpperChar}{toUpperChar}IPSUM"); + fullSetOfData.Add($"lorem ipsum{nonCapChar}", $"LOREM IPSUM{toUpperChar}"); + fullSetOfData.Add($"lorem ipsum{nonCapChar}{nonCapChar}", $"LOREM IPSUM{toUpperChar}{toUpperChar}"); + } - // All the cases from https://github.com/tc39/test262/blob/main/test/intl402/String/prototype/toLocaleUpperCase/special_casing_Lithuanian.js - public TheoryData TestData() + // All the cases from https://github.com/tc39/test262/blob/main/test/intl402/String/prototype/toLocaleUpperCase/special_casing_Lithuanian.js + public TheoryData TestData() + { + // COMBINING DOT ABOVE (U+0307) not removed when uppercasing capital I + AddStringsForChars("İ", "İ"); + // COMBINING DOT ABOVE (U+0307) not removed when uppercasing capital J + AddStringsForChars("J̇", "J̇"); + for (int i = 0; i < softDotted.Length; i++) { - // COMBINING DOT ABOVE (U+0307) not removed when uppercasing capital I - AddStringsForChars("İ", "İ"); - // COMBINING DOT ABOVE (U+0307) not removed when uppercasing capital J - AddStringsForChars("J̇", "J̇"); - for (int i = 0; i < softDotted.Length; i++) - { - // COMBINING DOT ABOVE (U+0307) removed when preceded by Soft_Dotted. - // Character directly preceded by Soft_Dotted. - AddStringsForChars(softDotted[i] + "\u0307", softDottedUpperCased[i]); + // COMBINING DOT ABOVE (U+0307) removed when preceded by Soft_Dotted. + // Character directly preceded by Soft_Dotted. + AddStringsForChars(softDotted[i] + "\u0307", softDottedUpperCased[i]); - // COMBINING DOT ABOVE (U+0307) removed if preceded by Soft_Dotted. - // Character not directly preceded by Soft_Dotted. - // - COMBINING DOT BELOW (U+0323), combining class 220 (Below) - AddStringsForChars(softDotted[i] + "\u0323\u0307", softDottedUpperCased[i] + "\u0323"); + // COMBINING DOT ABOVE (U+0307) removed if preceded by Soft_Dotted. + // Character not directly preceded by Soft_Dotted. + // - COMBINING DOT BELOW (U+0323), combining class 220 (Below) + AddStringsForChars(softDotted[i] + "\u0323\u0307", softDottedUpperCased[i] + "\u0323"); - // COMBINING DOT ABOVE removed if preceded by Soft_Dotted. - // Character not directly preceded by Soft_Dotted. - // - PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE (U+101FD = D800 DDFD), combining class 220 (Below) - AddStringsForChars(softDotted[i] + "\uD800\uDDFD\u0307", softDottedUpperCased[i] + "\uD800\uDDFD"); - } - - return fullSetOfData; + // COMBINING DOT ABOVE removed if preceded by Soft_Dotted. + // Character not directly preceded by Soft_Dotted. + // - PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE (U+101FD = D800 DDFD), combining class 220 (Below) + AddStringsForChars(softDotted[i] + "\uD800\uDDFD\u0307", softDottedUpperCased[i] + "\uD800\uDDFD"); } + + return fullSetOfData; } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/TestClasses/AsyncTestClass.cs b/Jint.Tests/Runtime/TestClasses/AsyncTestClass.cs index 19a640423e..d294a9d6e8 100644 --- a/Jint.Tests/Runtime/TestClasses/AsyncTestClass.cs +++ b/Jint.Tests/Runtime/TestClasses/AsyncTestClass.cs @@ -1,40 +1,39 @@ -namespace Jint.Tests.Runtime.TestClasses +namespace Jint.Tests.Runtime.TestClasses; + +internal class AsyncTestClass { - internal class AsyncTestClass - { - public static readonly string TestString = "Hello World"; + public static readonly string TestString = "Hello World"; - public string StringToAppend { get; set; } = string.Empty; + public string StringToAppend { get; set; } = string.Empty; - public async Task AddToStringDelayedAsync(string appendWith) - { - await Task.Delay(1000).ConfigureAwait(false); + public async Task AddToStringDelayedAsync(string appendWith) + { + await Task.Delay(1000).ConfigureAwait(false); - StringToAppend += appendWith; - } + StringToAppend += appendWith; + } - public async Task ReturnDelayedTaskAsync() - { - await Task.Delay(1000).ConfigureAwait(false); + public async Task ReturnDelayedTaskAsync() + { + await Task.Delay(1000).ConfigureAwait(false); - return TestString; - } + return TestString; + } - public Task ReturnCompletedTask() - { - return Task.FromResult(TestString); - } + public Task ReturnCompletedTask() + { + return Task.FromResult(TestString); + } - public Task ReturnCancelledTask(CancellationToken token) - { - return Task.FromCanceled(token); - } + public Task ReturnCancelledTask(CancellationToken token) + { + return Task.FromCanceled(token); + } - public async Task ThrowAfterDelayAsync() - { - await Task.Delay(100).ConfigureAwait(false); + public async Task ThrowAfterDelayAsync() + { + await Task.Delay(100).ConfigureAwait(false); - throw new Exception("Task threw exception"); - } + throw new Exception("Task threw exception"); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/TestClasses/HelloWorld.cs b/Jint.Tests/Runtime/TestClasses/HelloWorld.cs index 34f1256663..3ac181f207 100644 --- a/Jint.Tests/Runtime/TestClasses/HelloWorld.cs +++ b/Jint.Tests/Runtime/TestClasses/HelloWorld.cs @@ -1,11 +1,10 @@ -namespace Jint.Tests.Runtime.TestClasses +namespace Jint.Tests.Runtime.TestClasses; + +public class HelloWorld { - public class HelloWorld + public void ThrowException() { - public void ThrowException() - { - int zero = 0; - int x = 5 / zero; - } + int zero = 0; + int x = 5 / zero; } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/TypeConverterTests.cs b/Jint.Tests/Runtime/TypeConverterTests.cs index 0cd85c4921..781c893995 100644 --- a/Jint.Tests/Runtime/TypeConverterTests.cs +++ b/Jint.Tests/Runtime/TypeConverterTests.cs @@ -2,87 +2,87 @@ using Jint.Runtime; using Xunit.Abstractions; -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class TypeConverterTests { - public class TypeConverterTests - { - private readonly Engine _engine; + private readonly Engine _engine; - public TypeConverterTests(ITestOutputHelper output) - { - _engine = new Engine() + public TypeConverterTests(ITestOutputHelper output) + { + _engine = new Engine() .SetValue("log", new Action(o => output.WriteLine(o.ToString()))) .SetValue("assert", new Action(Assert.True)) .SetValue("equal", new Action(Assert.Equal)) - ; - } + ; + } - public static readonly IEnumerable ConvertNumberToInt32AndUint32TestData = new TheoryData() - { - { 0.0, 0 }, - { -0.0, 0 }, - { double.Epsilon, 0 }, - { 0.5, 0 }, - { -0.5, 0 }, - { 0.9999999999999999, 0 }, - { 1.0, 1 }, - { 1.5, 1 }, - { 10.0, 10 }, - { -12.3, -12 }, - { 1485772.6, 1485772 }, - { -984737183.8, -984737183 }, + public static readonly IEnumerable ConvertNumberToInt32AndUint32TestData = new TheoryData() + { + { 0.0, 0 }, + { -0.0, 0 }, + { double.Epsilon, 0 }, + { 0.5, 0 }, + { -0.5, 0 }, + { 0.9999999999999999, 0 }, + { 1.0, 1 }, + { 1.5, 1 }, + { 10.0, 10 }, + { -12.3, -12 }, + { 1485772.6, 1485772 }, + { -984737183.8, -984737183 }, - { Math.Pow(2, 31) - 1.0, int.MaxValue }, - { Math.Pow(2, 31) - 0.5, int.MaxValue }, - { Math.Pow(2, 32) - 1.0, -1 }, - { Math.Pow(2, 32) - 0.5, -1 }, - { Math.Pow(2, 32), 0 }, - { -Math.Pow(2, 32), 0 }, - { -Math.Pow(2, 32) - 0.5, 0 }, - { Math.Pow(2, 32) + 1.0, 1 }, - { Math.Pow(2, 45) + 17.56, 17 }, - { Math.Pow(2, 45) - 17.56, -18 }, - { -Math.Pow(2, 45) + 17.56, 18 }, - { Math.Pow(2, 51) + 17.5, 17 }, - { Math.Pow(2, 51) - 17.5, -18 }, + { Math.Pow(2, 31) - 1.0, int.MaxValue }, + { Math.Pow(2, 31) - 0.5, int.MaxValue }, + { Math.Pow(2, 32) - 1.0, -1 }, + { Math.Pow(2, 32) - 0.5, -1 }, + { Math.Pow(2, 32), 0 }, + { -Math.Pow(2, 32), 0 }, + { -Math.Pow(2, 32) - 0.5, 0 }, + { Math.Pow(2, 32) + 1.0, 1 }, + { Math.Pow(2, 45) + 17.56, 17 }, + { Math.Pow(2, 45) - 17.56, -18 }, + { -Math.Pow(2, 45) + 17.56, 18 }, + { Math.Pow(2, 51) + 17.5, 17 }, + { Math.Pow(2, 51) - 17.5, -18 }, - { Math.Pow(2, 53) - 1.0, -1 }, - { -Math.Pow(2, 53) + 1.0, 1 }, - { Math.Pow(2, 53), 0 }, - { -Math.Pow(2, 53), 0 }, - { Math.Pow(2, 53) + 12.0, 12 }, - { -Math.Pow(2, 53) - 12.0, -12 }, + { Math.Pow(2, 53) - 1.0, -1 }, + { -Math.Pow(2, 53) + 1.0, 1 }, + { Math.Pow(2, 53), 0 }, + { -Math.Pow(2, 53), 0 }, + { Math.Pow(2, 53) + 12.0, 12 }, + { -Math.Pow(2, 53) - 12.0, -12 }, - { (Math.Pow(2, 53) - 1.0) * Math.Pow(2, 1), -2 }, - { -(Math.Pow(2, 53) - 1.0) * Math.Pow(2, 3), 8 }, - { -(Math.Pow(2, 53) - 1.0) * Math.Pow(2, 11), 1 << 11 }, - { (Math.Pow(2, 53) - 1.0) * Math.Pow(2, 20), -(1 << 20) }, - { (Math.Pow(2, 53) - 1.0) * Math.Pow(2, 31), int.MinValue }, - { -(Math.Pow(2, 53) - 1.0) * Math.Pow(2, 31), int.MinValue }, - { (Math.Pow(2, 53) - 1.0) * Math.Pow(2, 32), 0 }, - { -(Math.Pow(2, 53) - 1.0) * Math.Pow(2, 32), 0 }, - { (Math.Pow(2, 53) - 1.0) * Math.Pow(2, 36), 0 }, + { (Math.Pow(2, 53) - 1.0) * Math.Pow(2, 1), -2 }, + { -(Math.Pow(2, 53) - 1.0) * Math.Pow(2, 3), 8 }, + { -(Math.Pow(2, 53) - 1.0) * Math.Pow(2, 11), 1 << 11 }, + { (Math.Pow(2, 53) - 1.0) * Math.Pow(2, 20), -(1 << 20) }, + { (Math.Pow(2, 53) - 1.0) * Math.Pow(2, 31), int.MinValue }, + { -(Math.Pow(2, 53) - 1.0) * Math.Pow(2, 31), int.MinValue }, + { (Math.Pow(2, 53) - 1.0) * Math.Pow(2, 32), 0 }, + { -(Math.Pow(2, 53) - 1.0) * Math.Pow(2, 32), 0 }, + { (Math.Pow(2, 53) - 1.0) * Math.Pow(2, 36), 0 }, - { double.MaxValue, 0 }, - { double.MinValue, 0 }, - { double.PositiveInfinity, 0 }, - { double.NegativeInfinity, 0 }, - { double.NaN, 0 }, - }; + { double.MaxValue, 0 }, + { double.MinValue, 0 }, + { double.PositiveInfinity, 0 }, + { double.NegativeInfinity, 0 }, + { double.NaN, 0 }, + }; - [Theory] - [MemberData(nameof(ConvertNumberToInt32AndUint32TestData))] - public void ConvertNumberToInt32AndUint32(double value, int expectedResult) - { - JsValue jsval = value; - Assert.Equal(expectedResult, TypeConverter.ToInt32(jsval)); - Assert.Equal((uint)expectedResult, TypeConverter.ToUint32(jsval)); - } + [Theory] + [MemberData(nameof(ConvertNumberToInt32AndUint32TestData))] + public void ConvertNumberToInt32AndUint32(double value, int expectedResult) + { + JsValue jsval = value; + Assert.Equal(expectedResult, TypeConverter.ToInt32(jsval)); + Assert.Equal((uint)expectedResult, TypeConverter.ToUint32(jsval)); + } - [Fact] - public void ToPrimitiveShouldEvaluateOnlyOnceDuringInExpression() - { - _engine.Execute(@" + [Fact] + public void ToPrimitiveShouldEvaluateOnlyOnceDuringInExpression() + { + _engine.Execute(@" var b = {}; var bval = 0; b[Symbol.toPrimitive] = function(hint) { return bval++; }; @@ -90,7 +90,5 @@ public void ToPrimitiveShouldEvaluateOnlyOnceDuringInExpression() b in {}; equal(1, bval); "); - } } - -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/TypedArrayInteropTests.cs b/Jint.Tests/Runtime/TypedArrayInteropTests.cs index 572d04e5ff..ce0a4a2161 100644 --- a/Jint.Tests/Runtime/TypedArrayInteropTests.cs +++ b/Jint.Tests/Runtime/TypedArrayInteropTests.cs @@ -1,124 +1,124 @@ -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class TypedArrayInteropTests { - public class TypedArrayInteropTests + [Fact] + public void CanInteropWithInt8() { - [Fact] - public void CanInteropWithInt8() - { - var engine = new Engine(); - var source = new sbyte[] { 42, 12 }; - engine.SetValue("testSubject", engine.Realm.Intrinsics.Int8Array.Construct(source)); - ValidateCreatedTypeArray(engine, "Int8Array"); - - var fromEngine = engine.GetValue("testSubject"); - Assert.True(fromEngine.IsInt8Array()); - Assert.Equal(source, fromEngine.AsInt8Array()); - } - - [Fact] - public void CanInteropWithUint8() - { - var engine = new Engine(); - var source = new byte[] { 42, 12 }; - engine.SetValue("testSubject", engine.Realm.Intrinsics.Uint8Array.Construct(source)); - ValidateCreatedTypeArray(engine, "Uint8Array"); - - var fromEngine = engine.GetValue("testSubject"); - Assert.True(fromEngine.IsUint8Array()); - Assert.Equal(source, fromEngine.AsUint8Array()); - } - - [Fact] - public void CanInteropWithUint8Clamped() - { - var engine = new Engine(); - var source = new byte[] { 42, 12 }; - engine.SetValue("testSubject", engine.Realm.Intrinsics.Uint8ClampedArray.Construct(source)); - ValidateCreatedTypeArray(engine, "Uint8ClampedArray"); - - var fromEngine = engine.GetValue("testSubject"); - Assert.True(fromEngine.IsUint8ClampedArray()); - Assert.Equal(source, fromEngine.AsUint8ClampedArray()); - } - - [Fact] - public void CanInteropWithInt16() - { - var engine = new Engine(); - var source = new short[] { 42, 12 }; - engine.SetValue("testSubject", engine.Realm.Intrinsics.Int16Array.Construct(source)); - ValidateCreatedTypeArray(engine, "Int16Array"); - - var fromEngine = engine.GetValue("testSubject"); - Assert.True(fromEngine.IsInt16Array()); - Assert.Equal(source, fromEngine.AsInt16Array()); - } - - [Fact] - public void CanInteropWithUint16() - { - var engine = new Engine(); - var source = new ushort[] { 42, 12 }; - engine.SetValue("testSubject", engine.Realm.Intrinsics.Uint16Array.Construct(source)); - ValidateCreatedTypeArray(engine, "Uint16Array"); + var engine = new Engine(); + var source = new sbyte[] { 42, 12 }; + engine.SetValue("testSubject", engine.Realm.Intrinsics.Int8Array.Construct(source)); + ValidateCreatedTypeArray(engine, "Int8Array"); + + var fromEngine = engine.GetValue("testSubject"); + Assert.True(fromEngine.IsInt8Array()); + Assert.Equal(source, fromEngine.AsInt8Array()); + } - var fromEngine = engine.GetValue("testSubject"); - Assert.True(fromEngine.IsUint16Array()); - Assert.Equal(source, fromEngine.AsUint16Array()); - } + [Fact] + public void CanInteropWithUint8() + { + var engine = new Engine(); + var source = new byte[] { 42, 12 }; + engine.SetValue("testSubject", engine.Realm.Intrinsics.Uint8Array.Construct(source)); + ValidateCreatedTypeArray(engine, "Uint8Array"); + + var fromEngine = engine.GetValue("testSubject"); + Assert.True(fromEngine.IsUint8Array()); + Assert.Equal(source, fromEngine.AsUint8Array()); + } - [Fact] - public void CanInteropWithInt32() - { - var engine = new Engine(); - var source = new[] { 42, 12 }; - engine.SetValue("testSubject", engine.Realm.Intrinsics.Int32Array.Construct(source)); - ValidateCreatedTypeArray(engine, "Int32Array"); + [Fact] + public void CanInteropWithUint8Clamped() + { + var engine = new Engine(); + var source = new byte[] { 42, 12 }; + engine.SetValue("testSubject", engine.Realm.Intrinsics.Uint8ClampedArray.Construct(source)); + ValidateCreatedTypeArray(engine, "Uint8ClampedArray"); + + var fromEngine = engine.GetValue("testSubject"); + Assert.True(fromEngine.IsUint8ClampedArray()); + Assert.Equal(source, fromEngine.AsUint8ClampedArray()); + } - var fromEngine = engine.GetValue("testSubject"); - Assert.True(fromEngine.IsInt32Array()); - Assert.Equal(source, fromEngine.AsInt32Array()); - } + [Fact] + public void CanInteropWithInt16() + { + var engine = new Engine(); + var source = new short[] { 42, 12 }; + engine.SetValue("testSubject", engine.Realm.Intrinsics.Int16Array.Construct(source)); + ValidateCreatedTypeArray(engine, "Int16Array"); + + var fromEngine = engine.GetValue("testSubject"); + Assert.True(fromEngine.IsInt16Array()); + Assert.Equal(source, fromEngine.AsInt16Array()); + } - [Fact] - public void CanInteropWithUint32() - { - var engine = new Engine(); - var source = new uint[] { 42, 12 }; + [Fact] + public void CanInteropWithUint16() + { + var engine = new Engine(); + var source = new ushort[] { 42, 12 }; + engine.SetValue("testSubject", engine.Realm.Intrinsics.Uint16Array.Construct(source)); + ValidateCreatedTypeArray(engine, "Uint16Array"); + + var fromEngine = engine.GetValue("testSubject"); + Assert.True(fromEngine.IsUint16Array()); + Assert.Equal(source, fromEngine.AsUint16Array()); + } - engine.SetValue("testSubject", engine.Realm.Intrinsics.Uint32Array.Construct(source)); - ValidateCreatedTypeArray(engine, "Uint32Array"); + [Fact] + public void CanInteropWithInt32() + { + var engine = new Engine(); + var source = new[] { 42, 12 }; + engine.SetValue("testSubject", engine.Realm.Intrinsics.Int32Array.Construct(source)); + ValidateCreatedTypeArray(engine, "Int32Array"); + + var fromEngine = engine.GetValue("testSubject"); + Assert.True(fromEngine.IsInt32Array()); + Assert.Equal(source, fromEngine.AsInt32Array()); + } - var fromEngine = engine.GetValue("testSubject"); - Assert.True(fromEngine.IsUint32Array()); - Assert.Equal(source, fromEngine.AsUint32Array()); - } + [Fact] + public void CanInteropWithUint32() + { + var engine = new Engine(); + var source = new uint[] { 42, 12 }; - [Fact] - public void CanInteropWithBigInt64() - { - var engine = new Engine(); - var source = new long[] { 42, 12 }; - engine.SetValue("testSubject", engine.Realm.Intrinsics.BigInt64Array.Construct(source)); - ValidateCreatedBigIntegerTypeArray(engine, "BigInt64Array"); + engine.SetValue("testSubject", engine.Realm.Intrinsics.Uint32Array.Construct(source)); + ValidateCreatedTypeArray(engine, "Uint32Array"); - var fromEngine = engine.GetValue("testSubject"); - Assert.True(fromEngine.IsBigInt64Array()); - Assert.Equal(source, fromEngine.AsBigInt64Array()); - } + var fromEngine = engine.GetValue("testSubject"); + Assert.True(fromEngine.IsUint32Array()); + Assert.Equal(source, fromEngine.AsUint32Array()); + } - [Fact] - public void CanInteropWithBigUint64() - { - var engine = new Engine(); - var source = new ulong[] { 42, 12 }; - engine.SetValue("testSubject", engine.Realm.Intrinsics.BigUint64Array.Construct(source)); - ValidateCreatedBigIntegerTypeArray(engine, "BigUint64Array"); + [Fact] + public void CanInteropWithBigInt64() + { + var engine = new Engine(); + var source = new long[] { 42, 12 }; + engine.SetValue("testSubject", engine.Realm.Intrinsics.BigInt64Array.Construct(source)); + ValidateCreatedBigIntegerTypeArray(engine, "BigInt64Array"); + + var fromEngine = engine.GetValue("testSubject"); + Assert.True(fromEngine.IsBigInt64Array()); + Assert.Equal(source, fromEngine.AsBigInt64Array()); + } - var fromEngine = engine.GetValue("testSubject"); - Assert.True(fromEngine.IsBigUint64Array()); - Assert.Equal(source, fromEngine.AsBigUint64Array()); - } + [Fact] + public void CanInteropWithBigUint64() + { + var engine = new Engine(); + var source = new ulong[] { 42, 12 }; + engine.SetValue("testSubject", engine.Realm.Intrinsics.BigUint64Array.Construct(source)); + ValidateCreatedBigIntegerTypeArray(engine, "BigUint64Array"); + + var fromEngine = engine.GetValue("testSubject"); + Assert.True(fromEngine.IsBigUint64Array()); + Assert.Equal(source, fromEngine.AsBigUint64Array()); + } #if NET6_0_OR_GREATER [Fact] @@ -136,48 +136,47 @@ public void CanInteropWithFloat16() } #endif - [Fact] - public void CanInteropWithFloat32() - { - var engine = new Engine(); - var source = new float[] { 42f, 12f }; + [Fact] + public void CanInteropWithFloat32() + { + var engine = new Engine(); + var source = new float[] { 42f, 12f }; - engine.SetValue("testSubject", engine.Realm.Intrinsics.Float32Array.Construct(source)); - ValidateCreatedTypeArray(engine, "Float32Array"); + engine.SetValue("testSubject", engine.Realm.Intrinsics.Float32Array.Construct(source)); + ValidateCreatedTypeArray(engine, "Float32Array"); - var fromEngine = engine.GetValue("testSubject"); - Assert.True(fromEngine.IsFloat32Array()); - Assert.Equal(source, fromEngine.AsFloat32Array()); - } + var fromEngine = engine.GetValue("testSubject"); + Assert.True(fromEngine.IsFloat32Array()); + Assert.Equal(source, fromEngine.AsFloat32Array()); + } - [Fact] - public void CanInteropWithFloat64() - { - var engine = new Engine(); - var source = new double[] { 42f, 12f }; + [Fact] + public void CanInteropWithFloat64() + { + var engine = new Engine(); + var source = new double[] { 42f, 12f }; - engine.SetValue("testSubject", engine.Realm.Intrinsics.Float64Array.Construct(source)); - ValidateCreatedTypeArray(engine, "Float64Array"); + engine.SetValue("testSubject", engine.Realm.Intrinsics.Float64Array.Construct(source)); + ValidateCreatedTypeArray(engine, "Float64Array"); - var fromEngine = engine.GetValue("testSubject"); - Assert.True(fromEngine.IsFloat64Array()); - Assert.Equal(source, fromEngine.AsFloat64Array()); - } + var fromEngine = engine.GetValue("testSubject"); + Assert.True(fromEngine.IsFloat64Array()); + Assert.Equal(source, fromEngine.AsFloat64Array()); + } - private static void ValidateCreatedTypeArray(Engine engine, string arrayName) - { - Assert.Equal(arrayName, engine.Evaluate("testSubject.constructor.name").AsString()); - Assert.Equal(2, engine.Evaluate("testSubject.length").AsNumber()); - Assert.Equal(42, engine.Evaluate("testSubject[0]").AsNumber()); - Assert.Equal(12, engine.Evaluate("testSubject[1]").AsNumber()); - } + private static void ValidateCreatedTypeArray(Engine engine, string arrayName) + { + Assert.Equal(arrayName, engine.Evaluate("testSubject.constructor.name").AsString()); + Assert.Equal(2, engine.Evaluate("testSubject.length").AsNumber()); + Assert.Equal(42, engine.Evaluate("testSubject[0]").AsNumber()); + Assert.Equal(12, engine.Evaluate("testSubject[1]").AsNumber()); + } - private static void ValidateCreatedBigIntegerTypeArray(Engine engine, string arrayName) - { - Assert.Equal(arrayName, engine.Evaluate("testSubject.constructor.name").AsString()); - Assert.Equal(2, engine.Evaluate("testSubject.length").AsNumber()); - Assert.Equal(42, engine.Evaluate("testSubject[0]").AsBigInt()); - Assert.Equal(12, engine.Evaluate("testSubject[1]").AsBigInt()); - } + private static void ValidateCreatedBigIntegerTypeArray(Engine engine, string arrayName) + { + Assert.Equal(arrayName, engine.Evaluate("testSubject.constructor.name").AsString()); + Assert.Equal(2, engine.Evaluate("testSubject.length").AsNumber()); + Assert.Equal(42, engine.Evaluate("testSubject[0]").AsBigInt()); + Assert.Equal(12, engine.Evaluate("testSubject[1]").AsBigInt()); } -} +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/UnicodeTests.cs b/Jint.Tests/Runtime/UnicodeTests.cs index 944132a953..a3d8896ecd 100644 --- a/Jint.Tests/Runtime/UnicodeTests.cs +++ b/Jint.Tests/Runtime/UnicodeTests.cs @@ -1,63 +1,62 @@ -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class UnicodeTests { - public class UnicodeTests - { - private readonly Engine _engine; - - public UnicodeTests() - { - _engine = new Engine() - .SetValue("log", new Action(Console.WriteLine)) - .SetValue("assert", new Action(Assert.True)) - .SetValue("equal", new Action(Assert.Equal)); - } - - private void RunTest(string source) - { - _engine.Execute(source); - } - - [Fact] - public void EscapeUnicodeUnits() - { - var value = _engine.Evaluate(@"var a = '\uD83D\uDE80'; return a;").AsString(); - Assert.Equal("🚀", value); - } - - [Fact] - public void EscapeUnicodeEscaped() - { - var value = _engine.Evaluate(@"var a = '\u{1F680}'; return a;").AsString(); - Assert.Equal("🚀", value); - } - - [Fact] - public void UnicodeIdentifiers() - { - var value = _engine.Evaluate(@"const hello = 123; return hell\u{6F}").AsNumber(); - Assert.Equal(123, value); - } - - [Fact] - public void RegexDontParseUnicodeEscapesWithoutFlag() - { - var value = _engine.Evaluate(@"return /^\u{3}$/.test('uuu')").AsBoolean(); - Assert.True(value); - } - - [Fact] - public void RegexParseUnicodeEscapesWithFlag() - { - var value = _engine.Evaluate(@"return /^\u{3}$/u.test('uuu')").AsBoolean(); - Assert.False(value); - } - - [Fact] - public void RegexParseUnicodeDoesntChangeSource() - { - var value = _engine.Evaluate(@"return /a\u0041/.source").AsString(); - Assert.Equal("a\\u0041", value); - } - - } -} + private readonly Engine _engine; + + public UnicodeTests() + { + _engine = new Engine() + .SetValue("log", new Action(Console.WriteLine)) + .SetValue("assert", new Action(Assert.True)) + .SetValue("equal", new Action(Assert.Equal)); + } + + private void RunTest(string source) + { + _engine.Execute(source); + } + + [Fact] + public void EscapeUnicodeUnits() + { + var value = _engine.Evaluate(@"var a = '\uD83D\uDE80'; return a;").AsString(); + Assert.Equal("🚀", value); + } + + [Fact] + public void EscapeUnicodeEscaped() + { + var value = _engine.Evaluate(@"var a = '\u{1F680}'; return a;").AsString(); + Assert.Equal("🚀", value); + } + + [Fact] + public void UnicodeIdentifiers() + { + var value = _engine.Evaluate(@"const hello = 123; return hell\u{6F}").AsNumber(); + Assert.Equal(123, value); + } + + [Fact] + public void RegexDontParseUnicodeEscapesWithoutFlag() + { + var value = _engine.Evaluate(@"return /^\u{3}$/.test('uuu')").AsBoolean(); + Assert.True(value); + } + + [Fact] + public void RegexParseUnicodeEscapesWithFlag() + { + var value = _engine.Evaluate(@"return /^\u{3}$/u.test('uuu')").AsBoolean(); + Assert.False(value); + } + + [Fact] + public void RegexParseUnicodeDoesntChangeSource() + { + var value = _engine.Evaluate(@"return /a\u0041/.source").AsString(); + Assert.Equal("a\\u0041", value); + } + +} \ No newline at end of file diff --git a/Jint.Tests/Runtime/UuidTests.cs b/Jint.Tests/Runtime/UuidTests.cs index 3a401b245b..de9f847eaf 100644 --- a/Jint.Tests/Runtime/UuidTests.cs +++ b/Jint.Tests/Runtime/UuidTests.cs @@ -1,48 +1,47 @@ using Jint.Tests.Runtime.Domain; -namespace Jint.Tests.Runtime +namespace Jint.Tests.Runtime; + +public class UuidTests : IDisposable { - public class UuidTests : IDisposable - { - private readonly Engine _engine; + private readonly Engine _engine; - public UuidTests() - { - _engine = new Engine(o => o.AddObjectConverter(new UuidConverter())) + public UuidTests() + { + _engine = new Engine(o => o.AddObjectConverter(new UuidConverter())) .SetValue("copy", new Func(v => new Guid(v.ToByteArray()))) - ; - UuidConstructor.CreateUuidConstructor(_engine); - } - - void IDisposable.Dispose() - { - } - - private object RunTest(string source) - { - return _engine.Evaluate(source).ToObject(); - } - - [Fact] - public void Empty() - { - Assert.Equal(Guid.Empty, RunTest($"Uuid.parse('{Guid.Empty}')")); - Assert.Equal(Guid.Empty, RunTest($"Uuid.Empty")); - } - - [Fact] - public void Random() - { - var actual = RunTest($"new Uuid()"); - Assert.NotEqual(Guid.Empty, actual); - Assert.IsType(actual); - } - - [Fact] - public void Copy() - { - _engine.Evaluate("const g = new Uuid();"); - Assert.Equal(_engine.Evaluate("copy(g).toString()").AsString(), _engine.Evaluate("g.toString()").AsString()); - } + ; + UuidConstructor.CreateUuidConstructor(_engine); + } + + void IDisposable.Dispose() + { + } + + private object RunTest(string source) + { + return _engine.Evaluate(source).ToObject(); + } + + [Fact] + public void Empty() + { + Assert.Equal(Guid.Empty, RunTest($"Uuid.parse('{Guid.Empty}')")); + Assert.Equal(Guid.Empty, RunTest($"Uuid.Empty")); + } + + [Fact] + public void Random() + { + var actual = RunTest($"new Uuid()"); + Assert.NotEqual(Guid.Empty, actual); + Assert.IsType(actual); + } + + [Fact] + public void Copy() + { + _engine.Evaluate("const g = new Uuid();"); + Assert.Equal(_engine.Evaluate("copy(g).toString()").AsString(), _engine.Evaluate("g.toString()").AsString()); } -} +} \ No newline at end of file diff --git a/Jint/AstExtensions.cs b/Jint/AstExtensions.cs index a5583f1e3e..b891d43edf 100644 --- a/Jint/AstExtensions.cs +++ b/Jint/AstExtensions.cs @@ -9,541 +9,540 @@ using Jint.Runtime.Modules; using Environment = Jint.Runtime.Environments.Environment; -namespace Jint +namespace Jint; + +public static class AstExtensions { - public static class AstExtensions - { #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value - internal static readonly SourceLocation DefaultLocation; + internal static readonly SourceLocation DefaultLocation; #pragma warning restore CS0649 // Field is never assigned to, and will always have its default value - public static JsValue GetKey(this T property, Engine engine) where T : IProperty => GetKey(property.Key, engine, property.Computed); + public static JsValue GetKey(this T property, Engine engine) where T : IProperty => GetKey(property.Key, engine, property.Computed); - public static JsValue GetKey(this Expression expression, Engine engine, bool resolveComputed = false) + public static JsValue GetKey(this Expression expression, Engine engine, bool resolveComputed = false) + { + var key = TryGetKey(expression, engine, resolveComputed); + if (key is not null) { - var key = TryGetKey(expression, engine, resolveComputed); - if (key is not null) - { - return TypeConverter.ToPropertyKey(key); - } - - ExceptionHelper.ThrowArgumentException("Unable to extract correct key, node type: " + expression.Type); - return JsValue.Undefined; + return TypeConverter.ToPropertyKey(key); } - internal static JsValue TryGetKey(this T property, Engine engine) where T : IProperty + ExceptionHelper.ThrowArgumentException("Unable to extract correct key, node type: " + expression.Type); + return JsValue.Undefined; + } + + internal static JsValue TryGetKey(this T property, Engine engine) where T : IProperty + { + return TryGetKey(property.Key, engine, property.Computed); + } + + internal static JsValue TryGetKey(this T expression, Engine engine, bool resolveComputed) where T : Expression + { + JsValue key; + if (expression is Literal literal) { - return TryGetKey(property.Key, engine, property.Computed); + key = literal.Kind == TokenKind.NullLiteral ? JsValue.Null : LiteralKeyToString(literal); } - - internal static JsValue TryGetKey(this T expression, Engine engine, bool resolveComputed) where T : Expression + else if (!resolveComputed && expression is Identifier identifier) { - JsValue key; - if (expression is Literal literal) - { - key = literal.Kind == TokenKind.NullLiteral ? JsValue.Null : LiteralKeyToString(literal); - } - else if (!resolveComputed && expression is Identifier identifier) - { - key = identifier.Name; - } - else if (expression is PrivateIdentifier privateIdentifier) - { - key = engine.ExecutionContext.PrivateEnvironment!.Names[privateIdentifier]; - } - else if (resolveComputed) - { - return TryGetComputedPropertyKey(expression, engine); - } - else - { - key = JsValue.Undefined; - } - return key; - } - - internal static JsValue TryGetComputedPropertyKey(T expression, Engine engine) - where T : Expression - { - if (expression.Type is NodeType.Identifier - or NodeType.CallExpression - or NodeType.BinaryExpression - or NodeType.UpdateExpression - or NodeType.AssignmentExpression - or NodeType.UnaryExpression - or NodeType.MemberExpression - or NodeType.LogicalExpression - or NodeType.ConditionalExpression - or NodeType.ArrowFunctionExpression - or NodeType.FunctionExpression - or NodeType.YieldExpression - or NodeType.TemplateLiteral) - { - var context = engine._activeEvaluationContext ?? new EvaluationContext(engine); - return JintExpression.Build(expression).GetValue(context); - } - - return JsValue.Undefined; + key = identifier.Name; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool IsFunctionDefinition(this T node) where T : Node + else if (expression is PrivateIdentifier privateIdentifier) + { + key = engine.ExecutionContext.PrivateEnvironment!.Names[privateIdentifier]; + } + else if (resolveComputed) + { + return TryGetComputedPropertyKey(expression, engine); + } + else { - var type = node.Type; - return type - is NodeType.FunctionExpression - or NodeType.ArrowFunctionExpression - or NodeType.ClassExpression; + key = JsValue.Undefined; } + return key; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool IsStrict(this IFunction function) + internal static JsValue TryGetComputedPropertyKey(T expression, Engine engine) + where T : Expression + { + if (expression.Type is NodeType.Identifier + or NodeType.CallExpression + or NodeType.BinaryExpression + or NodeType.UpdateExpression + or NodeType.AssignmentExpression + or NodeType.UnaryExpression + or NodeType.MemberExpression + or NodeType.LogicalExpression + or NodeType.ConditionalExpression + or NodeType.ArrowFunctionExpression + or NodeType.FunctionExpression + or NodeType.YieldExpression + or NodeType.TemplateLiteral) { - return function.Body is FunctionBody { Strict: true }; + var context = engine._activeEvaluationContext ?? new EvaluationContext(engine); + return JintExpression.Build(expression).GetValue(context); } - /// - /// https://tc39.es/ecma262/#sec-static-semantics-isconstantdeclaration - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool IsConstantDeclaration(this Declaration d) + return JsValue.Undefined; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsFunctionDefinition(this T node) where T : Node + { + var type = node.Type; + return type + is NodeType.FunctionExpression + or NodeType.ArrowFunctionExpression + or NodeType.ClassExpression; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsStrict(this IFunction function) + { + return function.Body is FunctionBody { Strict: true }; + } + + /// + /// https://tc39.es/ecma262/#sec-static-semantics-isconstantdeclaration + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsConstantDeclaration(this Declaration d) + { + return d is VariableDeclaration { Kind: VariableDeclarationKind.Const }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool HasName(this T node) where T : Node + { + if (!node.IsFunctionDefinition()) { - return d is VariableDeclaration { Kind: VariableDeclarationKind.Const }; + return false; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool HasName(this T node) where T : Node + if (node is IFunction { Id: not null }) { - if (!node.IsFunctionDefinition()) - { - return false; - } + return true; + } - if (node is IFunction { Id: not null }) - { - return true; - } + if (node is ClassExpression { Id: not null }) + { + return true; + } - if (node is ClassExpression { Id: not null }) - { - return true; - } + return false; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsAnonymousFunctionDefinition(this T node) where T : Node + { + if (!node.IsFunctionDefinition()) + { return false; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool IsAnonymousFunctionDefinition(this T node) where T : Node + if (node.HasName()) { - if (!node.IsFunctionDefinition()) - { - return false; - } + return false; + } - if (node.HasName()) - { - return false; - } + return true; + } - return true; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsOptional(this T node) where T : Expression + { + return node is IChainElement { Optional: true }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static string LiteralKeyToString(Literal literal) + { + if (literal is StringLiteral stringLiteral) + { + return stringLiteral.Value; + } + // prevent conversion to scientific notation + else if (literal is NumericLiteral numericLiteral) + { + return TypeConverter.ToString(numericLiteral.Value); + } + else if (literal is BigIntLiteral bigIntLiteral) + { + return bigIntLiteral.Value.ToString(provider: null); + } + else + { + // We shouldn't ever reach this line in the case of a literal property key. + return Convert.ToString(literal.Value, provider: null) ?? ""; } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool IsOptional(this T node) where T : Expression + internal static void GetBoundNames(this VariableDeclaration variableDeclaration, List target) + { + ref readonly var declarations = ref variableDeclaration.Declarations; + for (var i = 0; i < declarations.Count; i++) { - return node is IChainElement { Optional: true }; + var declaration = declarations[i]; + GetBoundNames(declaration.Id, target); } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static string LiteralKeyToString(Literal literal) + internal static void GetBoundNames(this Node? parameter, List target) + { + if (parameter is null || parameter.Type == NodeType.Literal) { - if (literal is StringLiteral stringLiteral) - { - return stringLiteral.Value; - } - // prevent conversion to scientific notation - else if (literal is NumericLiteral numericLiteral) - { - return TypeConverter.ToString(numericLiteral.Value); - } - else if (literal is BigIntLiteral bigIntLiteral) - { - return bigIntLiteral.Value.ToString(provider: null); - } - else - { - // We shouldn't ever reach this line in the case of a literal property key. - return Convert.ToString(literal.Value, provider: null) ?? ""; - } + return; } - internal static void GetBoundNames(this VariableDeclaration variableDeclaration, List target) + // try to get away without a loop + if (parameter is Identifier id) { - ref readonly var declarations = ref variableDeclaration.Declarations; - for (var i = 0; i < declarations.Count; i++) - { - var declaration = declarations[i]; - GetBoundNames(declaration.Id, target); - } + target.Add(id.Name); + return; } - internal static void GetBoundNames(this Node? parameter, List target) + if (parameter is VariableDeclaration variableDeclaration) { - if (parameter is null || parameter.Type == NodeType.Literal) - { - return; - } + variableDeclaration.GetBoundNames(target); + return; + } - // try to get away without a loop - if (parameter is Identifier id) + while (true) + { + if (parameter is Identifier identifier) { - target.Add(id.Name); + target.Add(identifier.Name); return; } - if (parameter is VariableDeclaration variableDeclaration) + if (parameter is RestElement restElement) { - variableDeclaration.GetBoundNames(target); - return; + parameter = restElement.Argument; + continue; } - while (true) + if (parameter is ArrayPattern arrayPattern) { - if (parameter is Identifier identifier) - { - target.Add(identifier.Name); - return; - } - - if (parameter is RestElement restElement) + ref readonly var arrayPatternElements = ref arrayPattern.Elements; + for (var i = 0; i < arrayPatternElements.Count; i++) { - parameter = restElement.Argument; - continue; + var expression = arrayPatternElements[i]; + GetBoundNames(expression, target); } - - if (parameter is ArrayPattern arrayPattern) + } + else if (parameter is ObjectPattern objectPattern) + { + ref readonly var objectPatternProperties = ref objectPattern.Properties; + for (var i = 0; i < objectPatternProperties.Count; i++) { - ref readonly var arrayPatternElements = ref arrayPattern.Elements; - for (var i = 0; i < arrayPatternElements.Count; i++) + var property = objectPatternProperties[i]; + if (property is AssignmentProperty p) { - var expression = arrayPatternElements[i]; - GetBoundNames(expression, target); + GetBoundNames(p.Value, target); } - } - else if (parameter is ObjectPattern objectPattern) - { - ref readonly var objectPatternProperties = ref objectPattern.Properties; - for (var i = 0; i < objectPatternProperties.Count; i++) - { - var property = objectPatternProperties[i]; - if (property is AssignmentProperty p) - { - GetBoundNames(p.Value, target); - } - else - { - GetBoundNames((RestElement) property, target); - } - } - } - else if (parameter is AssignmentPattern assignmentPattern) - { - parameter = assignmentPattern.Left; - continue; - } - else if (parameter is ClassDeclaration classDeclaration) - { - var name = classDeclaration.Id?.Name; - if (name != null) + else { - target.Add(name); + GetBoundNames((RestElement) property, target); } } - break; } - } - - /// - /// https://tc39.es/ecma262/#sec-static-semantics-privateboundidentifiers - /// - internal static void PrivateBoundIdentifiers(this Node parameter, HashSet target) - { - if (parameter.Type == NodeType.PrivateIdentifier) + else if (parameter is AssignmentPattern assignmentPattern) { - target.Add((PrivateIdentifier) parameter); + parameter = assignmentPattern.Left; + continue; } - else if (parameter.Type is NodeType.AccessorProperty or NodeType.MethodDefinition or NodeType.PropertyDefinition) + else if (parameter is ClassDeclaration classDeclaration) { - if (((ClassProperty) parameter).Key is PrivateIdentifier privateKeyIdentifier) + var name = classDeclaration.Id?.Name; + if (name != null) { - target.Add(privateKeyIdentifier); - } - } - else if (parameter.Type == NodeType.ClassBody) - { - ref readonly var elements = ref ((ClassBody) parameter).Body; - for (var i = 0; i < elements.Count; i++) - { - var element = elements[i]; - PrivateBoundIdentifiers(element, target); + target.Add(name); } } + break; } + } - internal static void BindingInitialization( - this Node? expression, - EvaluationContext context, - JsValue value, - Environment env) + /// + /// https://tc39.es/ecma262/#sec-static-semantics-privateboundidentifiers + /// + internal static void PrivateBoundIdentifiers(this Node parameter, HashSet target) + { + if (parameter.Type == NodeType.PrivateIdentifier) { - if (expression is Identifier identifier) + target.Add((PrivateIdentifier) parameter); + } + else if (parameter.Type is NodeType.AccessorProperty or NodeType.MethodDefinition or NodeType.PropertyDefinition) + { + if (((ClassProperty) parameter).Key is PrivateIdentifier privateKeyIdentifier) { - var catchEnvRecord = (DeclarativeEnvironment) env; - catchEnvRecord.CreateMutableBindingAndInitialize(identifier.Name, canBeDeleted: false, value); + target.Add(privateKeyIdentifier); } - else if (expression is DestructuringPattern pattern) + } + else if (parameter.Type == NodeType.ClassBody) + { + ref readonly var elements = ref ((ClassBody) parameter).Body; + for (var i = 0; i < elements.Count; i++) { - DestructuringPatternAssignmentExpression.ProcessPatterns(context, pattern, value, env); + var element = elements[i]; + PrivateBoundIdentifiers(element, target); } } + } - /// - /// https://tc39.es/ecma262/#sec-runtime-semantics-definemethod - /// - internal static Record DefineMethod(this T m, ObjectInstance obj, ObjectInstance? functionPrototype = null) where T : IProperty + internal static void BindingInitialization( + this Node? expression, + EvaluationContext context, + JsValue value, + Environment env) + { + if (expression is Identifier identifier) { - var engine = obj.Engine; - var propKey = TypeConverter.ToPropertyKey(m.GetKey(engine)); - var intrinsics = engine.Realm.Intrinsics; - - var runningExecutionContext = engine.ExecutionContext; - var env = runningExecutionContext.LexicalEnvironment; - var privateEnv = runningExecutionContext.PrivateEnvironment; + var catchEnvRecord = (DeclarativeEnvironment) env; + catchEnvRecord.CreateMutableBindingAndInitialize(identifier.Name, canBeDeleted: false, value); + } + else if (expression is DestructuringPattern pattern) + { + DestructuringPatternAssignmentExpression.ProcessPatterns(context, pattern, value, env); + } + } - var prototype = functionPrototype ?? intrinsics.Function.PrototypeObject; - var function = m.Value as IFunction; - if (function is null) - { - ExceptionHelper.ThrowSyntaxError(engine.Realm); - } + /// + /// https://tc39.es/ecma262/#sec-runtime-semantics-definemethod + /// + internal static Record DefineMethod(this T m, ObjectInstance obj, ObjectInstance? functionPrototype = null) where T : IProperty + { + var engine = obj.Engine; + var propKey = TypeConverter.ToPropertyKey(m.GetKey(engine)); + var intrinsics = engine.Realm.Intrinsics; - var definition = new JintFunctionDefinition(function); - var closure = intrinsics.Function.OrdinaryFunctionCreate(prototype, definition, definition.ThisMode, env, privateEnv); - closure.MakeMethod(obj); + var runningExecutionContext = engine.ExecutionContext; + var env = runningExecutionContext.LexicalEnvironment; + var privateEnv = runningExecutionContext.PrivateEnvironment; - return new Record(propKey, closure); + var prototype = functionPrototype ?? intrinsics.Function.PrototypeObject; + var function = m.Value as IFunction; + if (function is null) + { + ExceptionHelper.ThrowSyntaxError(engine.Realm); } - internal static void GetImportEntries(this ImportDeclaration import, List importEntries, HashSet requestedModules) - { - var source = import.Source.Value; - var specifiers = import.Specifiers; - var attributes = GetAttributes(import.Attributes); - requestedModules.Add(new ModuleRequest(source, attributes)); + var definition = new JintFunctionDefinition(function); + var closure = intrinsics.Function.OrdinaryFunctionCreate(prototype, definition, definition.ThisMode, env, privateEnv); + closure.MakeMethod(obj); + + return new Record(propKey, closure); + } + + internal static void GetImportEntries(this ImportDeclaration import, List importEntries, HashSet requestedModules) + { + var source = import.Source.Value; + var specifiers = import.Specifiers; + var attributes = GetAttributes(import.Attributes); + requestedModules.Add(new ModuleRequest(source, attributes)); - foreach (var specifier in specifiers) + foreach (var specifier in specifiers) + { + switch (specifier) { - switch (specifier) - { - case ImportNamespaceSpecifier namespaceSpecifier: - importEntries.Add(new ImportEntry(new ModuleRequest(source, attributes), "*", namespaceSpecifier.Local.GetModuleKey())); - break; - case ImportSpecifier importSpecifier: - importEntries.Add(new ImportEntry(new ModuleRequest(source, attributes), importSpecifier.Imported.GetModuleKey(), importSpecifier.Local.GetModuleKey()!)); - break; - case ImportDefaultSpecifier defaultSpecifier: - importEntries.Add(new ImportEntry(new ModuleRequest(source, attributes), "default", defaultSpecifier.Local.GetModuleKey())); - break; - } + case ImportNamespaceSpecifier namespaceSpecifier: + importEntries.Add(new ImportEntry(new ModuleRequest(source, attributes), "*", namespaceSpecifier.Local.GetModuleKey())); + break; + case ImportSpecifier importSpecifier: + importEntries.Add(new ImportEntry(new ModuleRequest(source, attributes), importSpecifier.Imported.GetModuleKey(), importSpecifier.Local.GetModuleKey()!)); + break; + case ImportDefaultSpecifier defaultSpecifier: + importEntries.Add(new ImportEntry(new ModuleRequest(source, attributes), "default", defaultSpecifier.Local.GetModuleKey())); + break; } } + } - private static ModuleImportAttribute[] GetAttributes(in NodeList importAttributes) + private static ModuleImportAttribute[] GetAttributes(in NodeList importAttributes) + { + if (importAttributes.Count == 0) { - if (importAttributes.Count == 0) - { - return Array.Empty(); - } + return Array.Empty(); + } - var attributes = new ModuleImportAttribute[importAttributes.Count]; - for (var i = 0; i < importAttributes.Count; i++) - { - var attribute = importAttributes[i]; - var key = attribute.Key is Identifier identifier ? identifier.Name : ((StringLiteral) attribute.Key).Value; - attributes[i] = new ModuleImportAttribute(key, attribute.Value.Value); - } - return attributes; + var attributes = new ModuleImportAttribute[importAttributes.Count]; + for (var i = 0; i < importAttributes.Count; i++) + { + var attribute = importAttributes[i]; + var key = attribute.Key is Identifier identifier ? identifier.Name : ((StringLiteral) attribute.Key).Value; + attributes[i] = new ModuleImportAttribute(key, attribute.Value.Value); } + return attributes; + } - internal static void GetExportEntries(this ExportDeclaration export, List exportEntries, HashSet requestedModules) + internal static void GetExportEntries(this ExportDeclaration export, List exportEntries, HashSet requestedModules) + { + switch (export) { - switch (export) - { - case ExportDefaultDeclaration defaultDeclaration: - GetExportEntries(true, defaultDeclaration.Declaration, exportEntries); - break; - case ExportAllDeclaration allDeclaration: - //Note: there is a pending PR for Esprima to support exporting an imported modules content as a namespace i.e. 'export * as ns from "mod"' - requestedModules.Add(new ModuleRequest(allDeclaration.Source.Value, [])); - exportEntries.Add(new(allDeclaration.Exported?.GetModuleKey(), new ModuleRequest(allDeclaration.Source.Value, []), "*", null)); - break; - case ExportNamedDeclaration namedDeclaration: - ref readonly var specifiers = ref namedDeclaration.Specifiers; - if (specifiers.Count == 0) - { - ModuleRequest? moduleRequest = namedDeclaration.Source != null - ? new ModuleRequest(namedDeclaration.Source.Value, []) - : null; + case ExportDefaultDeclaration defaultDeclaration: + GetExportEntries(true, defaultDeclaration.Declaration, exportEntries); + break; + case ExportAllDeclaration allDeclaration: + //Note: there is a pending PR for Esprima to support exporting an imported modules content as a namespace i.e. 'export * as ns from "mod"' + requestedModules.Add(new ModuleRequest(allDeclaration.Source.Value, [])); + exportEntries.Add(new(allDeclaration.Exported?.GetModuleKey(), new ModuleRequest(allDeclaration.Source.Value, []), "*", null)); + break; + case ExportNamedDeclaration namedDeclaration: + ref readonly var specifiers = ref namedDeclaration.Specifiers; + if (specifiers.Count == 0) + { + ModuleRequest? moduleRequest = namedDeclaration.Source != null + ? new ModuleRequest(namedDeclaration.Source.Value, []) + : null; - GetExportEntries(false, namedDeclaration.Declaration!, exportEntries, moduleRequest); - } - else + GetExportEntries(false, namedDeclaration.Declaration!, exportEntries, moduleRequest); + } + else + { + for (var i = 0; i < specifiers.Count; i++) { - for (var i = 0; i < specifiers.Count; i++) + var specifier = specifiers[i]; + if (namedDeclaration.Source != null) { - var specifier = specifiers[i]; - if (namedDeclaration.Source != null) - { - exportEntries.Add(new(specifier.Exported.GetModuleKey(), new ModuleRequest(namedDeclaration.Source.Value, []), specifier.Local.GetModuleKey(), null)); - } - else - { - exportEntries.Add(new(specifier.Exported.GetModuleKey(), null, null, specifier.Local.GetModuleKey())); - } + exportEntries.Add(new(specifier.Exported.GetModuleKey(), new ModuleRequest(namedDeclaration.Source.Value, []), specifier.Local.GetModuleKey(), null)); + } + else + { + exportEntries.Add(new(specifier.Exported.GetModuleKey(), null, null, specifier.Local.GetModuleKey())); } } + } - if (namedDeclaration.Source is not null) - { - requestedModules.Add(new ModuleRequest(namedDeclaration.Source.Value, [])); - } + if (namedDeclaration.Source is not null) + { + requestedModules.Add(new ModuleRequest(namedDeclaration.Source.Value, [])); + } - break; - } + break; } + } - private static void GetExportEntries(bool defaultExport, StatementOrExpression declaration, List exportEntries, ModuleRequest? moduleRequest = null) - { - var names = GetExportNames(declaration); + private static void GetExportEntries(bool defaultExport, StatementOrExpression declaration, List exportEntries, ModuleRequest? moduleRequest = null) + { + var names = GetExportNames(declaration); - if (names.Count == 0) + if (names.Count == 0) + { + if (defaultExport) { - if (defaultExport) - { - exportEntries.Add(new("default", null, null, "*default*")); - } + exportEntries.Add(new("default", null, null, "*default*")); } - else + } + else + { + for (var i = 0; i < names.Count; i++) { - for (var i = 0; i < names.Count; i++) - { - var name = names[i]; - var exportName = defaultExport ? "default" : name.Name; - exportEntries.Add(new(exportName, moduleRequest, null, name)); - } + var name = names[i]; + var exportName = defaultExport ? "default" : name.Name; + exportEntries.Add(new(exportName, moduleRequest, null, name)); } } + } + + private static List GetExportNames(StatementOrExpression declaration) + { + var result = new List(); - private static List GetExportNames(StatementOrExpression declaration) + switch (declaration) { - var result = new List(); + case FunctionDeclaration functionDeclaration: + var funcName = functionDeclaration.Id?.Name; + if (funcName is not null) + { + result.Add(funcName); + } - switch (declaration) - { - case FunctionDeclaration functionDeclaration: - var funcName = functionDeclaration.Id?.Name; - if (funcName is not null) - { - result.Add(funcName); - } + break; + case ClassDeclaration classDeclaration: + var className = classDeclaration.Id?.Name; + if (className is not null) + { + result.Add(className); + } - break; - case ClassDeclaration classDeclaration: - var className = classDeclaration.Id?.Name; - if (className is not null) - { - result.Add(className); - } + break; + case VariableDeclaration variableDeclaration: + variableDeclaration.GetBoundNames(result); + break; + } - break; - case VariableDeclaration variableDeclaration: - variableDeclaration.GetBoundNames(result); - break; - } + return result; + } - return result; - } + private static string GetModuleKey(this Expression expression) + { + return (expression as Identifier)?.Name ?? ((StringLiteral) expression).Value; + } - private static string GetModuleKey(this Expression expression) - { - return (expression as Identifier)?.Name ?? ((StringLiteral) expression).Value; - } + internal readonly record struct Record(JsValue Key, ScriptFunction Closure); + + /// + /// Creates a dummy node that can be used when only location available and node is required. + /// + internal static Node CreateLocationNode(in SourceLocation location) + { + return new MinimalSyntaxElement(location); + } - internal readonly record struct Record(JsValue Key, ScriptFunction Closure); + /// + /// https://tc39.es/ecma262/#sec-static-semantics-allprivateidentifiersvalid + /// + internal static void AllPrivateIdentifiersValid(this Script script, Realm realm, HashSet? privateIdentifiers) + { + var validator = new PrivateIdentifierValidator(realm, privateIdentifiers); + validator.Visit(script); + } - /// - /// Creates a dummy node that can be used when only location available and node is required. - /// - internal static Node CreateLocationNode(in SourceLocation location) + private sealed class MinimalSyntaxElement : Node + { + public MinimalSyntaxElement(in SourceLocation location) : base(NodeType.Unknown) { - return new MinimalSyntaxElement(location); + Location = location; } - /// - /// https://tc39.es/ecma262/#sec-static-semantics-allprivateidentifiersvalid - /// - internal static void AllPrivateIdentifiersValid(this Script script, Realm realm, HashSet? privateIdentifiers) + protected override IEnumerator? GetChildNodes() => throw new NotImplementedException(); + protected override object? Accept(AstVisitor visitor) => throw new NotImplementedException(); + } + + private sealed class PrivateIdentifierValidator : AstVisitor + { + private readonly Realm _realm; + private HashSet? _privateNames; + + public PrivateIdentifierValidator(Realm realm, HashSet? privateNames) { - var validator = new PrivateIdentifierValidator(realm, privateIdentifiers); - validator.Visit(script); + _realm = realm; + _privateNames = privateNames; } - private sealed class MinimalSyntaxElement : Node + protected override object VisitPrivateIdentifier(PrivateIdentifier privateIdentifier) { - public MinimalSyntaxElement(in SourceLocation location) : base(NodeType.Unknown) + if (_privateNames is null || !_privateNames.Contains(privateIdentifier)) { - Location = location; + Throw(_realm, privateIdentifier); } - - protected override IEnumerator? GetChildNodes() => throw new NotImplementedException(); - protected override object? Accept(AstVisitor visitor) => throw new NotImplementedException(); + return privateIdentifier; } - private sealed class PrivateIdentifierValidator : AstVisitor + protected override object VisitClassBody(ClassBody classBody) { - private readonly Realm _realm; - private HashSet? _privateNames; - - public PrivateIdentifierValidator(Realm realm, HashSet? privateNames) - { - _realm = realm; - _privateNames = privateNames; - } - - protected override object VisitPrivateIdentifier(PrivateIdentifier privateIdentifier) - { - if (_privateNames is null || !_privateNames.Contains(privateIdentifier)) - { - Throw(_realm, privateIdentifier); - } - return privateIdentifier; - } - - protected override object VisitClassBody(ClassBody classBody) - { - var oldList = _privateNames; - _privateNames = new HashSet(PrivateIdentifierNameComparer._instance); - classBody.PrivateBoundIdentifiers(_privateNames); - base.VisitClassBody(classBody); - _privateNames = oldList; - return classBody; - } + var oldList = _privateNames; + _privateNames = new HashSet(PrivateIdentifierNameComparer._instance); + classBody.PrivateBoundIdentifiers(_privateNames); + base.VisitClassBody(classBody); + _privateNames = oldList; + return classBody; + } - [MethodImpl(MethodImplOptions.NoInlining)] - private static void Throw(Realm r, PrivateIdentifier id) - { - ExceptionHelper.ThrowSyntaxError(r, $"Private field '#{id.Name}' must be declared in an enclosing class"); - } + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Throw(Realm r, PrivateIdentifier id) + { + ExceptionHelper.ThrowSyntaxError(r, $"Private field '#{id.Name}' must be declared in an enclosing class"); } } -} +} \ No newline at end of file diff --git a/Jint/Collections/DictionarySlim.cs b/Jint/Collections/DictionarySlim.cs index 03729cd2d3..85f3320636 100644 --- a/Jint/Collections/DictionarySlim.cs +++ b/Jint/Collections/DictionarySlim.cs @@ -9,292 +9,291 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace Jint.Collections +namespace Jint.Collections; + +/// +/// DictionarySlim<string, TValue> is similar to Dictionary<TKey, TValue> but optimized in three ways: +/// 1) It allows access to the value by ref replacing the common TryGetValue and Add pattern. +/// 2) It does not store the hash code (assumes it is cheap to equate values). +/// 3) It does not accept an equality comparer (assumes Object.GetHashCode() and Object.Equals() or overridden implementation are cheap and sufficient). +/// +[DebuggerDisplay("Count = {Count}")] +internal class DictionarySlim : IReadOnlyCollection> where TKey : IEquatable { - /// - /// DictionarySlim<string, TValue> is similar to Dictionary<TKey, TValue> but optimized in three ways: - /// 1) It allows access to the value by ref replacing the common TryGetValue and Add pattern. - /// 2) It does not store the hash code (assumes it is cheap to equate values). - /// 3) It does not accept an equality comparer (assumes Object.GetHashCode() and Object.Equals() or overridden implementation are cheap and sufficient). - /// - [DebuggerDisplay("Count = {Count}")] - internal class DictionarySlim : IReadOnlyCollection> where TKey : IEquatable + // We want to initialize without allocating arrays. We also want to avoid null checks. + // Array.Empty would give divide by zero in modulo operation. So we use static one element arrays. + // The first add will cause a resize replacing these with real arrays of three elements. + // Arrays are wrapped in a class to avoid being duplicated for each + private static readonly Entry[] InitialEntries = new Entry[1]; + private int _count; + // 0-based index into _entries of head of free chain: -1 means empty + private int _freeList = -1; + // 1-based index into _entries; 0 means empty + private int[] _buckets; + private Entry[] _entries; + + [DebuggerDisplay("({key}, {value})->{next}")] + [StructLayout(LayoutKind.Auto)] + private struct Entry { - // We want to initialize without allocating arrays. We also want to avoid null checks. - // Array.Empty would give divide by zero in modulo operation. So we use static one element arrays. - // The first add will cause a resize replacing these with real arrays of three elements. - // Arrays are wrapped in a class to avoid being duplicated for each - private static readonly Entry[] InitialEntries = new Entry[1]; - private int _count; - // 0-based index into _entries of head of free chain: -1 means empty - private int _freeList = -1; - // 1-based index into _entries; 0 means empty - private int[] _buckets; - private Entry[] _entries; - - [DebuggerDisplay("({key}, {value})->{next}")] - [StructLayout(LayoutKind.Auto)] - private struct Entry - { - public TKey key; - public TValue value; - // 0-based index of next entry in chain: -1 means end of chain - // also encodes whether this entry _itself_ is part of the free list by changing sign and subtracting 3, - // so -2 means end of free list, -3 means index 0 but on free list, -4 means index 1 but on free list, etc. - public int next; - } + public TKey key; + public TValue value; + // 0-based index of next entry in chain: -1 means end of chain + // also encodes whether this entry _itself_ is part of the free list by changing sign and subtracting 3, + // so -2 means end of free list, -3 means index 0 but on free list, -4 means index 1 but on free list, etc. + public int next; + } - public DictionarySlim() - { - _buckets = HashHelpers.SizeOneIntArray; - _entries = InitialEntries; - } + public DictionarySlim() + { + _buckets = HashHelpers.SizeOneIntArray; + _entries = InitialEntries; + } - public DictionarySlim(int capacity) - { - if (capacity < 2) - capacity = 2; // 1 would indicate the dummy array - capacity = HashHelpers.PowerOf2(capacity); - _buckets = new int[capacity]; - _entries = new Entry[capacity]; - } + public DictionarySlim(int capacity) + { + if (capacity < 2) + capacity = 2; // 1 would indicate the dummy array + capacity = HashHelpers.PowerOf2(capacity); + _buckets = new int[capacity]; + _entries = new Entry[capacity]; + } + + public int Count => _count; - public int Count => _count; + /// + /// Clears the dictionary. Note that this invalidates any active enumerators. + /// + public void Clear() + { + _count = 0; + _freeList = -1; + _buckets = HashHelpers.SizeOneIntArray; + _entries = InitialEntries; + } - /// - /// Clears the dictionary. Note that this invalidates any active enumerators. - /// - public void Clear() + public bool ContainsKey(TKey key) + { + Entry[] entries = _entries; + for (int i = _buckets[key.GetHashCode() & (_buckets.Length-1)] - 1; + (uint)i < (uint)entries.Length; i = entries[i].next) { - _count = 0; - _freeList = -1; - _buckets = HashHelpers.SizeOneIntArray; - _entries = InitialEntries; + if (key.Equals(entries[i].key)) + return true; } - public bool ContainsKey(TKey key) + return false; + } + + public bool TryGetValue(TKey key, out TValue value) + { + Entry[] entries = _entries; + for (int i = _buckets[key.GetHashCode() & (_buckets.Length - 1)] - 1; + (uint)i < (uint)entries.Length; i = entries[i].next) { - Entry[] entries = _entries; - for (int i = _buckets[key.GetHashCode() & (_buckets.Length-1)] - 1; - (uint)i < (uint)entries.Length; i = entries[i].next) + if (key.Equals(entries[i].key)) { - if (key.Equals(entries[i].key)) - return true; + value = entries[i].value; + return true; } - - return false; } - public bool TryGetValue(TKey key, out TValue value) + value = default; + return false; + } + + public bool Remove(TKey key) + { + Entry[] entries = _entries; + int bucketIndex = key.GetHashCode() & (_buckets.Length - 1); + int entryIndex = _buckets[bucketIndex] - 1; + + int lastIndex = -1; + while (entryIndex != -1) { - Entry[] entries = _entries; - for (int i = _buckets[key.GetHashCode() & (_buckets.Length - 1)] - 1; - (uint)i < (uint)entries.Length; i = entries[i].next) + Entry candidate = entries[entryIndex]; + if (candidate.key.Equals(key)) { - if (key.Equals(entries[i].key)) - { - value = entries[i].value; - return true; + if (lastIndex != -1) + { // Fixup preceding element in chain to point to next (if any) + entries[lastIndex].next = candidate.next; + } + else + { // Fixup bucket to new head (if any) + _buckets[bucketIndex] = candidate.next + 1; } - } - value = default; - return false; - } + entries[entryIndex] = default; - public bool Remove(TKey key) - { - Entry[] entries = _entries; - int bucketIndex = key.GetHashCode() & (_buckets.Length - 1); - int entryIndex = _buckets[bucketIndex] - 1; + entries[entryIndex].next = -3 - _freeList; // New head of free list + _freeList = entryIndex; - int lastIndex = -1; - while (entryIndex != -1) - { - Entry candidate = entries[entryIndex]; - if (candidate.key.Equals(key)) - { - if (lastIndex != -1) - { // Fixup preceding element in chain to point to next (if any) - entries[lastIndex].next = candidate.next; - } - else - { // Fixup bucket to new head (if any) - _buckets[bucketIndex] = candidate.next + 1; - } - - entries[entryIndex] = default; - - entries[entryIndex].next = -3 - _freeList; // New head of free list - _freeList = entryIndex; - - _count--; - return true; - } - lastIndex = entryIndex; - entryIndex = candidate.next; + _count--; + return true; } - - return false; + lastIndex = entryIndex; + entryIndex = candidate.next; } - // Not safe for concurrent _reads_ (at least, if either of them add) - // For concurrent reads, prefer TryGetValue(key, out value) - /// - /// Gets the value for the specified key, or, if the key is not present, - /// adds an entry and returns the value by ref. This makes it possible to - /// add or update a value in a single look up operation. - /// - /// Key to look for - /// Reference to the new or existing value - public ref TValue GetOrAddValueRef(TKey key) - { - Entry[] entries = _entries; - int bucketIndex = key.GetHashCode() & (_buckets.Length - 1); - for (int i = _buckets[bucketIndex] - 1; - (uint)i < (uint)entries.Length; i = entries[i].next) - { - if (key.Equals(entries[i].key)) - return ref entries[i].value; - } + return false; + } - return ref AddKey(key, bucketIndex); + // Not safe for concurrent _reads_ (at least, if either of them add) + // For concurrent reads, prefer TryGetValue(key, out value) + /// + /// Gets the value for the specified key, or, if the key is not present, + /// adds an entry and returns the value by ref. This makes it possible to + /// add or update a value in a single look up operation. + /// + /// Key to look for + /// Reference to the new or existing value + public ref TValue GetOrAddValueRef(TKey key) + { + Entry[] entries = _entries; + int bucketIndex = key.GetHashCode() & (_buckets.Length - 1); + for (int i = _buckets[bucketIndex] - 1; + (uint)i < (uint)entries.Length; i = entries[i].next) + { + if (key.Equals(entries[i].key)) + return ref entries[i].value; } - public ref TValue this[TKey key] + return ref AddKey(key, bucketIndex); + } + + public ref TValue this[TKey key] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref GetOrAddValueRef(key); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private ref TValue AddKey(TKey key, int bucketIndex) + { + Entry[] entries = _entries; + int entryIndex; + if (_freeList != -1) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref GetOrAddValueRef(key); + entryIndex = _freeList; + _freeList = -3 - entries[_freeList].next; } - - [MethodImpl(MethodImplOptions.NoInlining)] - private ref TValue AddKey(TKey key, int bucketIndex) + else { - Entry[] entries = _entries; - int entryIndex; - if (_freeList != -1) + if (_count == entries.Length || entries.Length == 1) { - entryIndex = _freeList; - _freeList = -3 - entries[_freeList].next; + entries = Resize(); + bucketIndex = key.GetHashCode() & (_buckets.Length - 1); + // entry indexes were not changed by Resize } - else - { - if (_count == entries.Length || entries.Length == 1) - { - entries = Resize(); - bucketIndex = key.GetHashCode() & (_buckets.Length - 1); - // entry indexes were not changed by Resize - } - entryIndex = _count; - } - - entries[entryIndex].key = key; - entries[entryIndex].next = _buckets[bucketIndex] - 1; - _buckets[bucketIndex] = entryIndex + 1; - _count++; - return ref entries[entryIndex].value; + entryIndex = _count; } - private Entry[] Resize() - { - Debug.Assert(_entries.Length == _count || _entries.Length == 1); // We only copy _count, so if it's longer we will miss some - int count = _count; - int newSize = _entries.Length * 2; - if ((uint)newSize > (uint)int.MaxValue) // uint cast handles overflow - throw new InvalidOperationException("Capacity Overflow"); - - var entries = new Entry[newSize]; - Array.Copy(_entries, 0, entries, 0, count); + entries[entryIndex].key = key; + entries[entryIndex].next = _buckets[bucketIndex] - 1; + _buckets[bucketIndex] = entryIndex + 1; + _count++; + return ref entries[entryIndex].value; + } - var newBuckets = new int[entries.Length]; - while (count-- > 0) - { - int bucketIndex = entries[count].key.GetHashCode() & (newBuckets.Length - 1); - entries[count].next = newBuckets[bucketIndex] - 1; - newBuckets[bucketIndex] = count + 1; - } + private Entry[] Resize() + { + Debug.Assert(_entries.Length == _count || _entries.Length == 1); // We only copy _count, so if it's longer we will miss some + int count = _count; + int newSize = _entries.Length * 2; + if ((uint)newSize > (uint)int.MaxValue) // uint cast handles overflow + throw new InvalidOperationException("Capacity Overflow"); - _buckets = newBuckets; - _entries = entries; + var entries = new Entry[newSize]; + Array.Copy(_entries, 0, entries, 0, count); - return entries; + var newBuckets = new int[entries.Length]; + while (count-- > 0) + { + int bucketIndex = entries[count].key.GetHashCode() & (newBuckets.Length - 1); + entries[count].next = newBuckets[bucketIndex] - 1; + newBuckets[bucketIndex] = count + 1; } - /// - /// Gets an enumerator over the dictionary - /// - public Enumerator GetEnumerator() => new Enumerator(this); // avoid boxing + _buckets = newBuckets; + _entries = entries; + + return entries; + } + + /// + /// Gets an enumerator over the dictionary + /// + public Enumerator GetEnumerator() => new Enumerator(this); // avoid boxing + + /// + /// Gets an enumerator over the dictionary + /// + IEnumerator> IEnumerable>.GetEnumerator() => + new Enumerator(this); - /// - /// Gets an enumerator over the dictionary - /// - IEnumerator> IEnumerable>.GetEnumerator() => - new Enumerator(this); + /// + /// Gets an enumerator over the dictionary + /// + IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this); - /// - /// Gets an enumerator over the dictionary - /// - IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this); + public struct Enumerator : IEnumerator> + { + private readonly DictionarySlim _dictionary; + private int _index; + private int _count; + private KeyValuePair _current; - public struct Enumerator : IEnumerator> + internal Enumerator(DictionarySlim dictionary) { - private readonly DictionarySlim _dictionary; - private int _index; - private int _count; - private KeyValuePair _current; + _dictionary = dictionary; + _index = 0; + _count = _dictionary._count; + _current = default; + } - internal Enumerator(DictionarySlim dictionary) + public bool MoveNext() + { + if (_count == 0) { - _dictionary = dictionary; - _index = 0; - _count = _dictionary._count; _current = default; + return false; } - public bool MoveNext() - { - if (_count == 0) - { - _current = default; - return false; - } + _count--; - _count--; + while (_dictionary._entries[_index].next < -1) + _index++; - while (_dictionary._entries[_index].next < -1) - _index++; + _current = new KeyValuePair( + _dictionary._entries[_index].key, + _dictionary._entries[_index++].value); + return true; + } - _current = new KeyValuePair( - _dictionary._entries[_index].key, - _dictionary._entries[_index++].value); - return true; - } + public KeyValuePair Current => _current; - public KeyValuePair Current => _current; + object IEnumerator.Current => _current; - object IEnumerator.Current => _current; + void IEnumerator.Reset() + { + _index = 0; + _count = _dictionary._count; + _current = default; + } - void IEnumerator.Reset() - { - _index = 0; - _count = _dictionary._count; - _current = default; - } + public void Dispose() { } + } - public void Dispose() { } - } + internal static class HashHelpers + { + internal static readonly int[] SizeOneIntArray = new int[1]; - internal static class HashHelpers + internal static int PowerOf2(int v) { - internal static readonly int[] SizeOneIntArray = new int[1]; - - internal static int PowerOf2(int v) - { - if ((v & (v - 1)) == 0) return v; - int i = 2; - while (i < v) i <<= 1; - return i; - } + if ((v & (v - 1)) == 0) return v; + int i = 2; + while (i < v) i <<= 1; + return i; } } } diff --git a/Jint/Collections/HybridDictionary.cs b/Jint/Collections/HybridDictionary.cs index f629d0600e..b11bf0229b 100644 --- a/Jint/Collections/HybridDictionary.cs +++ b/Jint/Collections/HybridDictionary.cs @@ -3,249 +3,248 @@ using System.Collections; using System.Runtime.CompilerServices; -namespace Jint.Collections +namespace Jint.Collections; + +internal class HybridDictionary : IEnumerable> { - internal class HybridDictionary : IEnumerable> - { - private const int CutoverPoint = 9; - private const int InitialDictionarySize = 13; - private const int FixedSizeCutoverPoint = 6; + private const int CutoverPoint = 9; + private const int InitialDictionarySize = 13; + private const int FixedSizeCutoverPoint = 6; - private readonly bool _checkExistingKeys; - private ListDictionary _list; - internal StringDictionarySlim _dictionary; + private readonly bool _checkExistingKeys; + private ListDictionary _list; + internal StringDictionarySlim _dictionary; - public HybridDictionary() : this(0, checkExistingKeys: true) - { - } + public HybridDictionary() : this(0, checkExistingKeys: true) + { + } - public HybridDictionary(int initialSize, bool checkExistingKeys) + public HybridDictionary(int initialSize, bool checkExistingKeys) + { + _checkExistingKeys = checkExistingKeys; + if (initialSize >= FixedSizeCutoverPoint) { - _checkExistingKeys = checkExistingKeys; - if (initialSize >= FixedSizeCutoverPoint) - { - _dictionary = new StringDictionarySlim(initialSize); - } + _dictionary = new StringDictionarySlim(initialSize); } + } + + protected HybridDictionary(StringDictionarySlim dictionary) + { + _checkExistingKeys = true; + _dictionary = dictionary; + } - protected HybridDictionary(StringDictionarySlim dictionary) + public TValue this[Key key] + { + get { - _checkExistingKeys = true; - _dictionary = dictionary; + TryGetValue(key, out var value); + return value; } - - public TValue this[Key key] + set { - get + if (_dictionary != null) { - TryGetValue(key, out var value); - return value; + _dictionary[key] = value; } - set + else if (_list != null) { - if (_dictionary != null) + if (_list.Count >= CutoverPoint - 1) { - _dictionary[key] = value; - } - else if (_list != null) - { - if (_list.Count >= CutoverPoint - 1) - { - SwitchToDictionary(key, value, tryAdd: false); - } - else - { - _list[key] = value; - } + SwitchToDictionary(key, value, tryAdd: false); } else { - _list = new ListDictionary(key, value, _checkExistingKeys); + _list[key] = value; } } - } - - public bool TryGetValue(Key key, out TValue value) - { - if (_dictionary != null) - { - return _dictionary.TryGetValue(key, out value); - } - - if (_list != null) + else { - return _list.TryGetValue(key, out value); + _list = new ListDictionary(key, value, _checkExistingKeys); } + } + } - value = default; - return false; + public bool TryGetValue(Key key, out TValue value) + { + if (_dictionary != null) + { + return _dictionary.TryGetValue(key, out value); } - public void SetOrUpdateValue(Key key, Func updater, TState state) + if (_list != null) { - if (_dictionary != null) - { - _dictionary.SetOrUpdateValue(key, updater, state); - } - else if (_list != null) - { - _list.SetOrUpdateValue(key, updater, state); - } - else - { - _list = new ListDictionary(key, updater(default, state), _checkExistingKeys); - } + return _list.TryGetValue(key, out value); } - private bool SwitchToDictionary(Key key, TValue value, bool tryAdd) + value = default; + return false; + } + + public void SetOrUpdateValue(Key key, Func updater, TState state) + { + if (_dictionary != null) { - var dictionary = new StringDictionarySlim(InitialDictionarySize); - foreach (var pair in _list) - { - dictionary[pair.Key] = pair.Value; - } + _dictionary.SetOrUpdateValue(key, updater, state); + } + else if (_list != null) + { + _list.SetOrUpdateValue(key, updater, state); + } + else + { + _list = new ListDictionary(key, updater(default, state), _checkExistingKeys); + } + } - bool result; - if (tryAdd) - { - result = dictionary.TryAdd(key, value); - } - else - { - dictionary[key] = value; - result = true; - } - _dictionary = dictionary; - _list = null; - return result; + private bool SwitchToDictionary(Key key, TValue value, bool tryAdd) + { + var dictionary = new StringDictionarySlim(InitialDictionarySize); + foreach (var pair in _list) + { + dictionary[pair.Key] = pair.Value; } - public int Count + bool result; + if (tryAdd) + { + result = dictionary.TryAdd(key, value); + } + else { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _dictionary?.Count ?? _list?.Count ?? 0; + dictionary[key] = value; + result = true; } + _dictionary = dictionary; + _list = null; + return result; + } + + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _dictionary?.Count ?? _list?.Count ?? 0; + } - public bool TryAdd(Key key, TValue value) + public bool TryAdd(Key key, TValue value) + { + if (_dictionary != null) { - if (_dictionary != null) + return _dictionary.TryAdd(key, value); + } + else + { + _list ??= new ListDictionary(key, value, _checkExistingKeys); + + if (_list.Count + 1 >= CutoverPoint) { - return _dictionary.TryAdd(key, value); + return SwitchToDictionary(key, value, tryAdd: true); } else { - _list ??= new ListDictionary(key, value, _checkExistingKeys); - - if (_list.Count + 1 >= CutoverPoint) - { - return SwitchToDictionary(key, value, tryAdd: true); - } - else - { - return _list.Add(key, value, tryAdd: true); - } + return _list.Add(key, value, tryAdd: true); } } + } - public void Add(Key key, TValue value) + public void Add(Key key, TValue value) + { + if (_dictionary != null) { - if (_dictionary != null) + _dictionary.GetOrAddValueRef(key) = value; + } + else + { + if (_list == null) { - _dictionary.GetOrAddValueRef(key) = value; + _list = new ListDictionary(key, value, _checkExistingKeys); } else { - if (_list == null) + if (_list.Count + 1 >= CutoverPoint) { - _list = new ListDictionary(key, value, _checkExistingKeys); + SwitchToDictionary(key, value, tryAdd: false); } else { - if (_list.Count + 1 >= CutoverPoint) - { - SwitchToDictionary(key, value, tryAdd: false); - } - else - { - _list.Add(key, value); - } + _list.Add(key, value); } } } + } + + public void Clear() + { + _dictionary?.Clear(); + _list?.Clear(); + } - public void Clear() + public bool ContainsKey(Key key) + { + if (_dictionary != null) { - _dictionary?.Clear(); - _list?.Clear(); + return _dictionary.ContainsKey(key); } - public bool ContainsKey(Key key) + if (_list != null) { - if (_dictionary != null) - { - return _dictionary.ContainsKey(key); - } + return _list.ContainsKey(key); + } - if (_list != null) - { - return _list.ContainsKey(key); - } + return false; + } - return false; + IEnumerator> IEnumerable>.GetEnumerator() + { + if (_dictionary != null) + { + return _dictionary.GetEnumerator(); } - IEnumerator> IEnumerable>.GetEnumerator() + if (_list != null) { - if (_dictionary != null) - { - return _dictionary.GetEnumerator(); - } + return _list.GetEnumerator(); + } - if (_list != null) - { - return _list.GetEnumerator(); - } + return System.Linq.Enumerable.Empty>().GetEnumerator(); - return System.Linq.Enumerable.Empty>().GetEnumerator(); + } + IEnumerator IEnumerable.GetEnumerator() + { + if (_dictionary != null) + { + return _dictionary.GetEnumerator(); } - IEnumerator IEnumerable.GetEnumerator() + if (_list != null) { - if (_dictionary != null) - { - return _dictionary.GetEnumerator(); - } - - if (_list != null) - { - return _list.GetEnumerator(); - } - - return System.Linq.Enumerable.Empty>().GetEnumerator(); + return _list.GetEnumerator(); } - public bool Remove(Key key) - { - if (_dictionary != null) - { - return _dictionary.Remove(key); - } + return System.Linq.Enumerable.Empty>().GetEnumerator(); + } - return _list != null && _list.Remove(key); + public bool Remove(Key key) + { + if (_dictionary != null) + { + return _dictionary.Remove(key); } - /// - /// Optimization when no need to check for existing items. - /// - public bool CheckExistingKeys + return _list != null && _list.Remove(key); + } + + /// + /// Optimization when no need to check for existing items. + /// + public bool CheckExistingKeys + { + set { - set + if (_list != null) { - if (_list != null) - { - _list.CheckExistingKeys = value; - } + _list.CheckExistingKeys = value; } } } diff --git a/Jint/Collections/ListDictionary.cs b/Jint/Collections/ListDictionary.cs index 51e72c98ed..9ee4556369 100644 --- a/Jint/Collections/ListDictionary.cs +++ b/Jint/Collections/ListDictionary.cs @@ -4,264 +4,263 @@ using System.Runtime.CompilerServices; using Jint.Runtime; -namespace Jint.Collections -{ - internal sealed class ListDictionary : IEnumerable> - { - private DictionaryNode _head; - private int _count; - private bool _checkExistingKeys; +namespace Jint.Collections; - public ListDictionary(Key key, TValue value, bool checkExistingKeys) - { - _checkExistingKeys = checkExistingKeys; - _head = new DictionaryNode - { - Key = key, - Value = value - }; - _count = 1; - } +internal sealed class ListDictionary : IEnumerable> +{ + private DictionaryNode _head; + private int _count; + private bool _checkExistingKeys; - public TValue this[Key key] + public ListDictionary(Key key, TValue value, bool checkExistingKeys) + { + _checkExistingKeys = checkExistingKeys; + _head = new DictionaryNode { - get - { - TryGetValue(key, out var value); - return value; - } - set - { - DictionaryNode last = null; - DictionaryNode node; - var checkExistingKeys = _checkExistingKeys; - for (node = _head; node != null; node = node.Next) - { - var oldKey = node.Key; - if (checkExistingKeys && oldKey == key) - { - break; - } - - last = node; - } - - if (node != null) - { - // Found it - node.Value = value; - return; - } - - AddNode(key, value, last); - } - } + Key = key, + Value = value + }; + _count = 1; + } - public bool TryGetValue(Key key, out TValue value) + public TValue this[Key key] + { + get { - var node = _head; - while (node != null) - { - if (node.Key == key) - { - value = node.Value; - return true; - } - - node = node.Next; - } - - value = default; - return false; + TryGetValue(key, out var value); + return value; } - - public void SetOrUpdateValue(Key key, Func updater, TState state) + set { DictionaryNode last = null; DictionaryNode node; + var checkExistingKeys = _checkExistingKeys; for (node = _head; node != null; node = node.Next) { - if (node.Key == key) + var oldKey = node.Key; + if (checkExistingKeys && oldKey == key) { - node.Value = updater(node.Value, state); - return; + break; } last = node; } - AddNode(key, updater(default, state), last); - } + if (node != null) + { + // Found it + node.Value = value; + return; + } - public int Count - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _count; + AddNode(key, value, last); } + } - public bool Add(Key key, TValue value, bool tryAdd = false) + public bool TryGetValue(Key key, out TValue value) + { + var node = _head; + while (node != null) { - DictionaryNode last = null; - DictionaryNode node; - var checkExistingKeys = _checkExistingKeys; - for (node = _head; node != null; node = node.Next) + if (node.Key == key) { - var oldKey = node.Key; - if (checkExistingKeys && oldKey == key) - { - if (tryAdd) - { - return false; - } - ExceptionHelper.ThrowArgumentException(); - } - - last = node; + value = node.Value; + return true; } - AddNode(key, value, last); - return true; + node = node.Next; } - private void AddNode(Key key, TValue value, DictionaryNode last) + value = default; + return false; + } + + public void SetOrUpdateValue(Key key, Func updater, TState state) + { + DictionaryNode last = null; + DictionaryNode node; + for (node = _head; node != null; node = node.Next) { - var newNode = new DictionaryNode + if (node.Key == key) { - Key = key, - Value = value - }; - if (_head is null) - { - _head = newNode; + node.Value = updater(node.Value, state); + return; } - else - { - last.Next = newNode; - } - _count++; - } - public void Clear() - { - _count = 0; - _head = null; + last = node; } - public bool ContainsKey(Key key) + AddNode(key, updater(default, state), last); + } + + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _count; + } + + public bool Add(Key key, TValue value, bool tryAdd = false) + { + DictionaryNode last = null; + DictionaryNode node; + var checkExistingKeys = _checkExistingKeys; + for (node = _head; node != null; node = node.Next) { - for (var node = _head; node != null; node = node.Next) + var oldKey = node.Key; + if (checkExistingKeys && oldKey == key) { - var oldKey = node.Key; - if (oldKey == key) + if (tryAdd) { - return true; + return false; } + ExceptionHelper.ThrowArgumentException(); } - return false; + last = node; } - internal bool CheckExistingKeys - { - set => _checkExistingKeys = value; - } + AddNode(key, value, last); + return true; + } - public NodeEnumerator GetEnumerator() + private void AddNode(Key key, TValue value, DictionaryNode last) + { + var newNode = new DictionaryNode { - return new NodeEnumerator(this); - } - - IEnumerator> IEnumerable>.GetEnumerator() + Key = key, + Value = value + }; + if (_head is null) { - return new NodeEnumerator(this); + _head = newNode; } - - IEnumerator IEnumerable.GetEnumerator() + else { - return new NodeEnumerator(this); + last.Next = newNode; } + _count++; + } - public bool Remove(Key key) + public void Clear() + { + _count = 0; + _head = null; + } + + public bool ContainsKey(Key key) + { + for (var node = _head; node != null; node = node.Next) { - DictionaryNode last = null; - DictionaryNode node; - for (node = _head; node != null; node = node.Next) + var oldKey = node.Key; + if (oldKey == key) { - var oldKey = node.Key; - if (oldKey == key) - { - break; - } - - last = node; + return true; } + } - if (node == null) - { - return false; - } + return false; + } - if (node == _head) - { - _head = node.Next; - } - else + internal bool CheckExistingKeys + { + set => _checkExistingKeys = value; + } + + public NodeEnumerator GetEnumerator() + { + return new NodeEnumerator(this); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return new NodeEnumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new NodeEnumerator(this); + } + + public bool Remove(Key key) + { + DictionaryNode last = null; + DictionaryNode node; + for (node = _head; node != null; node = node.Next) + { + var oldKey = node.Key; + if (oldKey == key) { - last.Next = node.Next; + break; } - _count--; - return true; + last = node; } - internal struct NodeEnumerator : IEnumerator> + if (node == null) { - private readonly ListDictionary _list; - private DictionaryNode _current; - private bool _start; + return false; + } - public NodeEnumerator(ListDictionary list) - { - _list = list; - _start = true; - _current = null; - } + if (node == _head) + { + _head = node.Next; + } + else + { + last.Next = node.Next; + } - public KeyValuePair Current => new KeyValuePair(_current.Key, _current.Value); + _count--; + return true; + } - public bool MoveNext() - { - if (_start) - { - _current = _list._head; - _start = false; - } - else if (_current != null) - { - _current = _current.Next; - } + internal struct NodeEnumerator : IEnumerator> + { + private readonly ListDictionary _list; + private DictionaryNode _current; + private bool _start; - return _current != null; - } + public NodeEnumerator(ListDictionary list) + { + _list = list; + _start = true; + _current = null; + } - void IEnumerator.Reset() + public KeyValuePair Current => new KeyValuePair(_current.Key, _current.Value); + + public bool MoveNext() + { + if (_start) { - _start = true; - _current = null; + _current = _list._head; + _start = false; } - - public void Dispose() + else if (_current != null) { + _current = _current.Next; } - object IEnumerator.Current => _current; + return _current != null; + } + + void IEnumerator.Reset() + { + _start = true; + _current = null; } - internal sealed class DictionaryNode + public void Dispose() { - public Key Key; - public TValue Value; - public DictionaryNode Next; } + + object IEnumerator.Current => _current; + } + + internal sealed class DictionaryNode + { + public Key Key; + public TValue Value; + public DictionaryNode Next; } } diff --git a/Jint/Collections/ObjectTraverseStack.cs b/Jint/Collections/ObjectTraverseStack.cs index e3afc0a6b8..41cedbdd0c 100644 --- a/Jint/Collections/ObjectTraverseStack.cs +++ b/Jint/Collections/ObjectTraverseStack.cs @@ -1,39 +1,38 @@ using Jint.Native; using Jint.Runtime; -namespace Jint.Collections +namespace Jint.Collections; + +/// +/// Helps traversing objects and checks for cyclic references. +/// +internal sealed class ObjectTraverseStack { - /// - /// Helps traversing objects and checks for cyclic references. - /// - internal sealed class ObjectTraverseStack + private readonly Engine _engine; + private readonly Stack _stack = new(); + + public ObjectTraverseStack(Engine engine) { - private readonly Engine _engine; - private readonly Stack _stack = new(); + _engine = engine; + } - public ObjectTraverseStack(Engine engine) + public void Enter(JsValue value) + { + if (value is null) { - _engine = engine; + ExceptionHelper.ThrowArgumentNullException(nameof(value)); } - public void Enter(JsValue value) + if (_stack.Contains(value)) { - if (value is null) - { - ExceptionHelper.ThrowArgumentNullException(nameof(value)); - } - - if (_stack.Contains(value)) - { - ExceptionHelper.ThrowTypeError(_engine.Realm, "Cyclic reference detected."); - } - - _stack.Push(value); + ExceptionHelper.ThrowTypeError(_engine.Realm, "Cyclic reference detected."); } - public void Exit() - { - _stack.Pop(); - } + _stack.Push(value); + } + + public void Exit() + { + _stack.Pop(); } } diff --git a/Jint/Collections/PropertyDictionary.cs b/Jint/Collections/PropertyDictionary.cs index 41a8c54397..40277a5ba8 100644 --- a/Jint/Collections/PropertyDictionary.cs +++ b/Jint/Collections/PropertyDictionary.cs @@ -1,19 +1,18 @@ using Jint.Runtime.Descriptors; -namespace Jint.Collections +namespace Jint.Collections; + +internal sealed class PropertyDictionary : HybridDictionary { - internal sealed class PropertyDictionary : HybridDictionary + public PropertyDictionary() { - public PropertyDictionary() - { - } + } - public PropertyDictionary(int capacity, bool checkExistingKeys) : base(capacity, checkExistingKeys) - { - } + public PropertyDictionary(int capacity, bool checkExistingKeys) : base(capacity, checkExistingKeys) + { + } - public PropertyDictionary(StringDictionarySlim properties) : base(properties) - { - } + public PropertyDictionary(StringDictionarySlim properties) : base(properties) + { } } diff --git a/Jint/Collections/RefStack.cs b/Jint/Collections/RefStack.cs index 5827448ed6..de5e905e98 100644 --- a/Jint/Collections/RefStack.cs +++ b/Jint/Collections/RefStack.cs @@ -3,194 +3,193 @@ using System.Runtime.CompilerServices; using Jint.Runtime; -namespace Jint.Collections +namespace Jint.Collections; + +/// +/// Stack for struct types. +/// +internal sealed class RefStack : IEnumerable where T : struct { - /// - /// Stack for struct types. - /// - internal sealed class RefStack : IEnumerable where T : struct + internal T[] _array; + internal int _size; + + private const int DefaultCapacity = 2; + + public RefStack(int capacity = DefaultCapacity) { - internal T[] _array; - internal int _size; + _array = new T[capacity]; + _size = 0; + } - private const int DefaultCapacity = 2; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly T Peek() + { + return ref _array[_size - 1]; + } - public RefStack(int capacity = DefaultCapacity) - { - _array = new T[capacity]; - _size = 0; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly T Peek(int fromTop) + { + var index = _size - 1 - fromTop; + return ref _array[index]; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly T Peek() - { - return ref _array[_size - 1]; - } + public T this[int index] => _array[index]; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly T Peek(int fromTop) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPeek([NotNullWhen(true)] out T item) + { + if (_size > 0) { - var index = _size - 1 - fromTop; - return ref _array[index]; + item = _array[_size - 1]; + return true; } - public T this[int index] => _array[index]; + item = default; + return false; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryPeek([NotNullWhen(true)] out T item) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly T Pop() + { + if (_size == 0) { - if (_size > 0) - { - item = _array[_size - 1]; - return true; - } - - item = default; - return false; + ExceptionHelper.ThrowInvalidOperationException("stack is empty"); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref readonly T Pop() - { - if (_size == 0) - { - ExceptionHelper.ThrowInvalidOperationException("stack is empty"); - } + _size--; + return ref _array[_size]; + } - _size--; - return ref _array[_size]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Push(in T item) + { + if (_size == _array.Length) + { + EnsureCapacity(_size + 1); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Push(in T item) + _array[_size++] = item; + } + + private void EnsureCapacity(int min) + { + var array = _array; + if (array.Length < min) { - if (_size == _array.Length) + var newCapacity = array.Length == 0 + ? DefaultCapacity + : array.Length * 2; + + if (newCapacity < min) { - EnsureCapacity(_size + 1); + newCapacity = min; } - _array[_size++] = item; + Resize(newCapacity); } + } - private void EnsureCapacity(int min) + private void Resize(int value) + { + if (value != _array.Length) { - var array = _array; - if (array.Length < min) + if (value > 0) { - var newCapacity = array.Length == 0 - ? DefaultCapacity - : array.Length * 2; - - if (newCapacity < min) + var newItems = new T[value]; + if (_size > 0) { - newCapacity = min; + Array.Copy(_array, 0, newItems, 0, _size); } - Resize(newCapacity); + _array = newItems; } - } - - private void Resize(int value) - { - if (value != _array.Length) + else { - if (value > 0) - { - var newItems = new T[value]; - if (_size > 0) - { - Array.Copy(_array, 0, newItems, 0, _size); - } - - _array = newItems; - } - else - { - _array = Array.Empty(); - } + _array = Array.Empty(); } } + } - public void Clear() - { - _size = 0; - } + public void Clear() + { + _size = 0; + } - public Enumerator GetEnumerator() - { - return new Enumerator(this); - } + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } - IEnumerator IEnumerable.GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + internal struct Enumerator : IEnumerator + { + private readonly RefStack _stack; + private int _index; + private T? _currentElement; + + internal Enumerator(RefStack stack) { - return GetEnumerator(); + _stack = stack; + _index = -2; + _currentElement = default; } - IEnumerator IEnumerable.GetEnumerator() + public void Dispose() { - return GetEnumerator(); + _index = -1; } - internal struct Enumerator : IEnumerator + public bool MoveNext() { - private readonly RefStack _stack; - private int _index; - private T? _currentElement; - - internal Enumerator(RefStack stack) + bool returnValue; + if (_index == -2) { - _stack = stack; - _index = -2; - _currentElement = default; - } - - public void Dispose() - { - _index = -1; - } - - public bool MoveNext() - { - bool returnValue; - if (_index == -2) - { - // First call to enumerator. - _index = _stack._size - 1; - returnValue = (_index >= 0); - if (returnValue) - { - _currentElement = _stack._array[_index]; - } - return returnValue; - } - - if (_index == -1) - { - // End of enumeration. - return false; - } - - returnValue = (--_index >= 0); + // First call to enumerator. + _index = _stack._size - 1; + returnValue = (_index >= 0); if (returnValue) { _currentElement = _stack._array[_index]; } - else - { - _currentElement = default; - } return returnValue; } - public T Current => (T) _currentElement!; - - object? IEnumerator.Current => Current; + if (_index == -1) + { + // End of enumeration. + return false; + } - void IEnumerator.Reset() + returnValue = (--_index >= 0); + if (returnValue) + { + _currentElement = _stack._array[_index]; + } + else { - _index = -2; _currentElement = default; } + return returnValue; + } + + public T Current => (T) _currentElement!; + + object? IEnumerator.Current => Current; + + void IEnumerator.Reset() + { + _index = -2; + _currentElement = default; } } } diff --git a/Jint/Collections/StringDictionarySlim.cs b/Jint/Collections/StringDictionarySlim.cs index 2ff194f1ee..cc8ea11833 100644 --- a/Jint/Collections/StringDictionarySlim.cs +++ b/Jint/Collections/StringDictionarySlim.cs @@ -12,341 +12,340 @@ using System.Linq; using System.Runtime.CompilerServices; -namespace Jint.Collections +namespace Jint.Collections; + +/// +/// DictionarySlim<string, TValue> is similar to Dictionary<TKey, TValue> but optimized in three ways: +/// 1) It allows access to the value by ref replacing the common TryGetValue and Add pattern. +/// 2) It does not store the hash code (assumes it is cheap to equate values). +/// 3) It does not accept an equality comparer (assumes Object.GetHashCode() and Object.Equals() or overridden implementation are cheap and sufficient). +/// +[DebuggerTypeProxy(typeof(DictionarySlimDebugView<>))] +[DebuggerDisplay("Count = {Count}")] +internal sealed class StringDictionarySlim : IReadOnlyCollection> { - /// - /// DictionarySlim<string, TValue> is similar to Dictionary<TKey, TValue> but optimized in three ways: - /// 1) It allows access to the value by ref replacing the common TryGetValue and Add pattern. - /// 2) It does not store the hash code (assumes it is cheap to equate values). - /// 3) It does not accept an equality comparer (assumes Object.GetHashCode() and Object.Equals() or overridden implementation are cheap and sufficient). - /// - [DebuggerTypeProxy(typeof(DictionarySlimDebugView<>))] - [DebuggerDisplay("Count = {Count}")] - internal sealed class StringDictionarySlim : IReadOnlyCollection> + // We want to initialize without allocating arrays. We also want to avoid null checks. + // Array.Empty would give divide by zero in modulo operation. So we use static one element arrays. + // The first add will cause a resize replacing these with real arrays of three elements. + // Arrays are wrapped in a class to avoid being duplicated for each + private static readonly Entry[] InitialEntries = new Entry[1]; + private int _count; + // 0-based index into _entries of head of free chain: -1 means empty + private int _freeList = -1; + // 1-based index into _entries; 0 means empty + private int[] _buckets; + private Entry[] _entries; + + [DebuggerDisplay("({key}, {value})->{next}")] + private struct Entry { - // We want to initialize without allocating arrays. We also want to avoid null checks. - // Array.Empty would give divide by zero in modulo operation. So we use static one element arrays. - // The first add will cause a resize replacing these with real arrays of three elements. - // Arrays are wrapped in a class to avoid being duplicated for each - private static readonly Entry[] InitialEntries = new Entry[1]; - private int _count; - // 0-based index into _entries of head of free chain: -1 means empty - private int _freeList = -1; - // 1-based index into _entries; 0 means empty - private int[] _buckets; - private Entry[] _entries; - - [DebuggerDisplay("({key}, {value})->{next}")] - private struct Entry - { - public Key key; - public TValue value; - // 0-based index of next entry in chain: -1 means end of chain - // also encodes whether this entry _itself_ is part of the free list by changing sign and subtracting 3, - // so -2 means end of free list, -3 means index 0 but on free list, -4 means index 1 but on free list, etc. - public int next; - } + public Key key; + public TValue value; + // 0-based index of next entry in chain: -1 means end of chain + // also encodes whether this entry _itself_ is part of the free list by changing sign and subtracting 3, + // so -2 means end of free list, -3 means index 0 but on free list, -4 means index 1 but on free list, etc. + public int next; + } - public StringDictionarySlim() - { - _buckets = HashHelpers.SizeOneIntArray; - _entries = InitialEntries; - } + public StringDictionarySlim() + { + _buckets = HashHelpers.SizeOneIntArray; + _entries = InitialEntries; + } - public StringDictionarySlim(int capacity) - { - if (capacity < 2) - capacity = 2; // 1 would indicate the dummy array - capacity = HashHelpers.PowerOf2(capacity); - _buckets = new int[capacity]; - _entries = new Entry[capacity]; - } + public StringDictionarySlim(int capacity) + { + if (capacity < 2) + capacity = 2; // 1 would indicate the dummy array + capacity = HashHelpers.PowerOf2(capacity); + _buckets = new int[capacity]; + _entries = new Entry[capacity]; + } - public int Count - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _count; - } + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _count; + } + + /// + /// Clears the dictionary. Note that this invalidates any active enumerators. + /// + public void Clear() + { + _count = 0; + _freeList = -1; + _buckets = HashHelpers.SizeOneIntArray; + _entries = InitialEntries; + } - /// - /// Clears the dictionary. Note that this invalidates any active enumerators. - /// - public void Clear() + public bool ContainsKey(Key key) + { + Entry[] entries = _entries; + for (int i = _buckets[key.HashCode & (_buckets.Length-1)] - 1; + (uint)i < (uint)entries.Length; i = entries[i].next) { - _count = 0; - _freeList = -1; - _buckets = HashHelpers.SizeOneIntArray; - _entries = InitialEntries; + if (key.Name == entries[i].key.Name) + return true; } - public bool ContainsKey(Key key) + return false; + } + + public bool TryGetValue(Key key, out TValue value) + { + Entry[] entries = _entries; + for (int i = _buckets[key.HashCode & (_buckets.Length - 1)] - 1; + (uint)i < (uint)entries.Length; i = entries[i].next) { - Entry[] entries = _entries; - for (int i = _buckets[key.HashCode & (_buckets.Length-1)] - 1; - (uint)i < (uint)entries.Length; i = entries[i].next) + if (key.Name == entries[i].key.Name) { - if (key.Name == entries[i].key.Name) - return true; + value = entries[i].value; + return true; } - - return false; } - public bool TryGetValue(Key key, out TValue value) + value = default; + return false; + } + + public bool Remove(Key key) + { + Entry[] entries = _entries; + int bucketIndex = key.HashCode & (_buckets.Length - 1); + int entryIndex = _buckets[bucketIndex] - 1; + + int lastIndex = -1; + while (entryIndex != -1) { - Entry[] entries = _entries; - for (int i = _buckets[key.HashCode & (_buckets.Length - 1)] - 1; - (uint)i < (uint)entries.Length; i = entries[i].next) + Entry candidate = entries[entryIndex]; + if (candidate.key == key) { - if (key.Name == entries[i].key.Name) - { - value = entries[i].value; - return true; + if (lastIndex != -1) + { // Fixup preceding element in chain to point to next (if any) + entries[lastIndex].next = candidate.next; + } + else + { // Fixup bucket to new head (if any) + _buckets[bucketIndex] = candidate.next + 1; } - } - value = default; - return false; - } + entries[entryIndex] = default; - public bool Remove(Key key) - { - Entry[] entries = _entries; - int bucketIndex = key.HashCode & (_buckets.Length - 1); - int entryIndex = _buckets[bucketIndex] - 1; + entries[entryIndex].next = -3 - _freeList; // New head of free list + _freeList = entryIndex; - int lastIndex = -1; - while (entryIndex != -1) - { - Entry candidate = entries[entryIndex]; - if (candidate.key == key) - { - if (lastIndex != -1) - { // Fixup preceding element in chain to point to next (if any) - entries[lastIndex].next = candidate.next; - } - else - { // Fixup bucket to new head (if any) - _buckets[bucketIndex] = candidate.next + 1; - } - - entries[entryIndex] = default; - - entries[entryIndex].next = -3 - _freeList; // New head of free list - _freeList = entryIndex; - - _count--; - return true; - } - lastIndex = entryIndex; - entryIndex = candidate.next; + _count--; + return true; } - - return false; + lastIndex = entryIndex; + entryIndex = candidate.next; } - public void SetOrUpdateValue(Key key, Func updater, TState state) + return false; + } + + public void SetOrUpdateValue(Key key, Func updater, TState state) + { + ref var currentValue = ref GetOrAddValueRef(key); + currentValue = updater(currentValue, state); + } + + // Not safe for concurrent _reads_ (at least, if either of them add) + // For concurrent reads, prefer TryGetValue(key, out value) + /// + /// Gets the value for the specified key, or, if the key is not present, + /// adds an entry and returns the value by ref. This makes it possible to + /// add or update a value in a single look up operation. + /// + /// Key to look for + /// Reference to the new or existing value + public ref TValue GetOrAddValueRef(Key key) + { + Entry[] entries = _entries; + int bucketIndex = key.HashCode & (_buckets.Length - 1); + for (int i = _buckets[bucketIndex] - 1; + (uint)i < (uint)entries.Length; i = entries[i].next) { - ref var currentValue = ref GetOrAddValueRef(key); - currentValue = updater(currentValue, state); + if (key.Name == entries[i].key.Name) + return ref entries[i].value; } - // Not safe for concurrent _reads_ (at least, if either of them add) - // For concurrent reads, prefer TryGetValue(key, out value) - /// - /// Gets the value for the specified key, or, if the key is not present, - /// adds an entry and returns the value by ref. This makes it possible to - /// add or update a value in a single look up operation. - /// - /// Key to look for - /// Reference to the new or existing value - public ref TValue GetOrAddValueRef(Key key) + return ref AddKey(key, bucketIndex); + } + + public bool TryAdd(Key key, TValue value) + { + Entry[] entries = _entries; + int bucketIndex = key.HashCode & (_buckets.Length - 1); + for (int i = _buckets[bucketIndex] - 1; + (uint)i < (uint)entries.Length; i = entries[i].next) { - Entry[] entries = _entries; - int bucketIndex = key.HashCode & (_buckets.Length - 1); - for (int i = _buckets[bucketIndex] - 1; - (uint)i < (uint)entries.Length; i = entries[i].next) + if (key.Name == entries[i].key.Name) { - if (key.Name == entries[i].key.Name) - return ref entries[i].value; + return false; } - - return ref AddKey(key, bucketIndex); } - public bool TryAdd(Key key, TValue value) - { - Entry[] entries = _entries; - int bucketIndex = key.HashCode & (_buckets.Length - 1); - for (int i = _buckets[bucketIndex] - 1; - (uint)i < (uint)entries.Length; i = entries[i].next) - { - if (key.Name == entries[i].key.Name) - { - return false; - } - } + AddKey(key, bucketIndex) = value; + return true; + } - AddKey(key, bucketIndex) = value; - return true; - } + /// + /// Adds a new item and expects key to not to exist. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddDangerous(in Key key, TValue value) + { + AddKey(key, key.HashCode & (_buckets.Length - 1)) = value; + } - /// - /// Adds a new item and expects key to not to exist. - /// + public ref TValue this[Key key] + { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddDangerous(in Key key, TValue value) - { - AddKey(key, key.HashCode & (_buckets.Length - 1)) = value; - } + get => ref GetOrAddValueRef(key); + } - public ref TValue this[Key key] + [MethodImpl(MethodImplOptions.NoInlining)] + private ref TValue AddKey(Key key, int bucketIndex) + { + Entry[] entries = _entries; + int entryIndex; + if (_freeList != -1) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref GetOrAddValueRef(key); + entryIndex = _freeList; + _freeList = -3 - entries[_freeList].next; } - - [MethodImpl(MethodImplOptions.NoInlining)] - private ref TValue AddKey(Key key, int bucketIndex) + else { - Entry[] entries = _entries; - int entryIndex; - if (_freeList != -1) - { - entryIndex = _freeList; - _freeList = -3 - entries[_freeList].next; - } - else + if (_count == entries.Length || entries.Length == 1) { - if (_count == entries.Length || entries.Length == 1) - { - entries = Resize(); - bucketIndex = key.HashCode & (_buckets.Length - 1); - // entry indexes were not changed by Resize - } - entryIndex = _count; + entries = Resize(); + bucketIndex = key.HashCode & (_buckets.Length - 1); + // entry indexes were not changed by Resize } - - entries[entryIndex].key = key; - entries[entryIndex].next = _buckets[bucketIndex] - 1; - _buckets[bucketIndex] = entryIndex + 1; - _count++; - return ref entries[entryIndex].value; + entryIndex = _count; } - private Entry[] Resize() - { - Debug.Assert(_entries.Length == _count || _entries.Length == 1); // We only copy _count, so if it's longer we will miss some - int count = _count; - int newSize = _entries.Length * 2; - if ((uint)newSize > (uint)int.MaxValue) // uint cast handles overflow - throw new InvalidOperationException("Capacity Overflow"); - - var entries = new Entry[newSize]; - Array.Copy(_entries, 0, entries, 0, count); + entries[entryIndex].key = key; + entries[entryIndex].next = _buckets[bucketIndex] - 1; + _buckets[bucketIndex] = entryIndex + 1; + _count++; + return ref entries[entryIndex].value; + } - var newBuckets = new int[entries.Length]; - while (count-- > 0) - { - int bucketIndex = entries[count].key.HashCode & (newBuckets.Length - 1); - entries[count].next = newBuckets[bucketIndex] - 1; - newBuckets[bucketIndex] = count + 1; - } + private Entry[] Resize() + { + Debug.Assert(_entries.Length == _count || _entries.Length == 1); // We only copy _count, so if it's longer we will miss some + int count = _count; + int newSize = _entries.Length * 2; + if ((uint)newSize > (uint)int.MaxValue) // uint cast handles overflow + throw new InvalidOperationException("Capacity Overflow"); - _buckets = newBuckets; - _entries = entries; + var entries = new Entry[newSize]; + Array.Copy(_entries, 0, entries, 0, count); - return entries; + var newBuckets = new int[entries.Length]; + while (count-- > 0) + { + int bucketIndex = entries[count].key.HashCode & (newBuckets.Length - 1); + entries[count].next = newBuckets[bucketIndex] - 1; + newBuckets[bucketIndex] = count + 1; } - /// - /// Gets an enumerator over the dictionary - /// - public Enumerator GetEnumerator() => new Enumerator(this); // avoid boxing + _buckets = newBuckets; + _entries = entries; + + return entries; + } - /// - /// Gets an enumerator over the dictionary - /// - IEnumerator> IEnumerable>.GetEnumerator() => - new Enumerator(this); + /// + /// Gets an enumerator over the dictionary + /// + public Enumerator GetEnumerator() => new Enumerator(this); // avoid boxing + + /// + /// Gets an enumerator over the dictionary + /// + IEnumerator> IEnumerable>.GetEnumerator() => + new Enumerator(this); + + /// + /// Gets an enumerator over the dictionary + /// + IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this); - /// - /// Gets an enumerator over the dictionary - /// - IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this); + public struct Enumerator : IEnumerator> + { + private readonly StringDictionarySlim _dictionary; + private int _index; + private int _count; + private KeyValuePair _current; - public struct Enumerator : IEnumerator> + internal Enumerator(StringDictionarySlim dictionary) { - private readonly StringDictionarySlim _dictionary; - private int _index; - private int _count; - private KeyValuePair _current; + _dictionary = dictionary; + _index = 0; + _count = _dictionary._count; + _current = default; + } - internal Enumerator(StringDictionarySlim dictionary) + public bool MoveNext() + { + if (_count == 0) { - _dictionary = dictionary; - _index = 0; - _count = _dictionary._count; _current = default; + return false; } - public bool MoveNext() - { - if (_count == 0) - { - _current = default; - return false; - } - - _count--; + _count--; - while (_dictionary._entries[_index].next < -1) - _index++; + while (_dictionary._entries[_index].next < -1) + _index++; - _current = new KeyValuePair( - _dictionary._entries[_index].key, - _dictionary._entries[_index++].value); - return true; - } + _current = new KeyValuePair( + _dictionary._entries[_index].key, + _dictionary._entries[_index++].value); + return true; + } - public KeyValuePair Current => _current; + public KeyValuePair Current => _current; - object IEnumerator.Current => _current; + object IEnumerator.Current => _current; - void IEnumerator.Reset() - { - _index = 0; - _count = _dictionary._count; - _current = default; - } - - public void Dispose() { } + void IEnumerator.Reset() + { + _index = 0; + _count = _dictionary._count; + _current = default; } - internal static class HashHelpers - { - internal static readonly int[] SizeOneIntArray = new int[1]; + public void Dispose() { } + } - internal static int PowerOf2(int v) - { - if ((v & (v - 1)) == 0) return v; - int i = 2; - while (i < v) i <<= 1; - return i; - } - } + internal static class HashHelpers + { + internal static readonly int[] SizeOneIntArray = new int[1]; - internal sealed class DictionarySlimDebugView + internal static int PowerOf2(int v) { - private readonly StringDictionarySlim _dictionary; + if ((v & (v - 1)) == 0) return v; + int i = 2; + while (i < v) i <<= 1; + return i; + } + } - public DictionarySlimDebugView(StringDictionarySlim dictionary) - { - _dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); - } + internal sealed class DictionarySlimDebugView + { + private readonly StringDictionarySlim _dictionary; - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public KeyValuePair[] Items => _dictionary.ToArray(); + public DictionarySlimDebugView(StringDictionarySlim dictionary) + { + _dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public KeyValuePair[] Items => _dictionary.ToArray(); } } diff --git a/Jint/Collections/SymbolDictionary.cs b/Jint/Collections/SymbolDictionary.cs index 182cf9ba2e..1627ec7e83 100644 --- a/Jint/Collections/SymbolDictionary.cs +++ b/Jint/Collections/SymbolDictionary.cs @@ -1,16 +1,15 @@ using Jint.Native; using Jint.Runtime.Descriptors; -namespace Jint.Collections +namespace Jint.Collections; + +internal sealed class SymbolDictionary : DictionarySlim { - internal sealed class SymbolDictionary : DictionarySlim + public SymbolDictionary() { - public SymbolDictionary() - { - } + } - public SymbolDictionary(int capacity) : base(capacity) - { - } + public SymbolDictionary(int capacity) : base(capacity) + { } } diff --git a/Jint/DeclarationBindingType.cs b/Jint/DeclarationBindingType.cs index 6f5a2fac41..c69c277066 100644 --- a/Jint/DeclarationBindingType.cs +++ b/Jint/DeclarationBindingType.cs @@ -1,9 +1,8 @@ -namespace Jint +namespace Jint; + +public enum DeclarationBindingType { - public enum DeclarationBindingType - { - GlobalCode, - FunctionCode, - EvalCode - } -} + GlobalCode, + FunctionCode, + EvalCode +} \ No newline at end of file diff --git a/Jint/Engine.cs b/Jint/Engine.cs index eae724f7b6..d023d64714 100644 --- a/Jint/Engine.cs +++ b/Jint/Engine.cs @@ -20,1592 +20,1591 @@ using Jint.Runtime.Interpreter.Expressions; using Environment = Jint.Runtime.Environments.Environment; -namespace Jint +namespace Jint; + +/// +/// Engine is the main API to JavaScript interpretation. Engine instances are not thread-safe. +/// +[DebuggerTypeProxy(typeof(EngineDebugView))] +public sealed partial class Engine : IDisposable { - /// - /// Engine is the main API to JavaScript interpretation. Engine instances are not thread-safe. - /// - [DebuggerTypeProxy(typeof(EngineDebugView))] - public sealed partial class Engine : IDisposable - { - private static readonly Options _defaultEngineOptions = new(); + private static readonly Options _defaultEngineOptions = new(); - private readonly Parser _defaultParser; - private ParserOptions? _defaultModuleParserOptions; // cache default ParserOptions for ModuleBuilder instances + private readonly Parser _defaultParser; + private ParserOptions? _defaultModuleParserOptions; // cache default ParserOptions for ModuleBuilder instances - private readonly ExecutionContextStack _executionContexts; - private JsValue _completionValue = JsValue.Undefined; - internal EvaluationContext? _activeEvaluationContext; - internal ErrorDispatchInfo? _error; + private readonly ExecutionContextStack _executionContexts; + private JsValue _completionValue = JsValue.Undefined; + internal EvaluationContext? _activeEvaluationContext; + internal ErrorDispatchInfo? _error; - private readonly EventLoop _eventLoop = new(); + private readonly EventLoop _eventLoop = new(); - private readonly Agent _agent = new(); + private readonly Agent _agent = new(); - // lazy properties - private DebugHandler? _debugger; + // lazy properties + private DebugHandler? _debugger; - // cached access - internal readonly IObjectConverter[]? _objectConverters; - internal readonly Constraint[] _constraints; - internal readonly bool _isDebugMode; - internal readonly bool _isStrict; - internal readonly IReferenceResolver _referenceResolver; - internal readonly ReferencePool _referencePool; - internal readonly ArgumentsInstancePool _argumentsInstancePool; - internal readonly JsValueArrayPool _jsValueArrayPool; - internal readonly ExtensionMethodCache _extensionMethods; + // cached access + internal readonly IObjectConverter[]? _objectConverters; + internal readonly Constraint[] _constraints; + internal readonly bool _isDebugMode; + internal readonly bool _isStrict; + internal readonly IReferenceResolver _referenceResolver; + internal readonly ReferencePool _referencePool; + internal readonly ArgumentsInstancePool _argumentsInstancePool; + internal readonly JsValueArrayPool _jsValueArrayPool; + internal readonly ExtensionMethodCache _extensionMethods; - public ITypeConverter TypeConverter { get; internal set; } + public ITypeConverter TypeConverter { get; internal set; } - // cache of types used when resolving CLR type names - internal readonly Dictionary TypeCache = new(StringComparer.Ordinal); + // cache of types used when resolving CLR type names + internal readonly Dictionary TypeCache = new(StringComparer.Ordinal); - // we use registered type reference as prototype if it's known - internal Dictionary? _typeReferences; + // we use registered type reference as prototype if it's known + internal Dictionary? _typeReferences; - // cache for already wrapped CLR objects to keep object identity - internal ConditionalWeakTable? _objectWrapperCache; + // cache for already wrapped CLR objects to keep object identity + internal ConditionalWeakTable? _objectWrapperCache; - internal readonly JintCallStack CallStack; - internal readonly StackGuard _stackGuard; + internal readonly JintCallStack CallStack; + internal readonly StackGuard _stackGuard; - // needed in initial engine setup, for example CLR function construction - internal Intrinsics _originalIntrinsics = null!; - internal Host _host = null!; + // needed in initial engine setup, for example CLR function construction + internal Intrinsics _originalIntrinsics = null!; + internal Host _host = null!; - // we need to cache reflection accessors on engine level as configuration options can affect outcome - internal readonly record struct ClrPropertyDescriptorFactoriesKey(Type Type, Key PropertyName); - internal Dictionary _reflectionAccessors = new(); + // we need to cache reflection accessors on engine level as configuration options can affect outcome + internal readonly record struct ClrPropertyDescriptorFactoriesKey(Type Type, Key PropertyName); + internal Dictionary _reflectionAccessors = new(); - /// - /// Constructs a new engine instance. - /// - public Engine() : this(null, null) - { - } + /// + /// Constructs a new engine instance. + /// + public Engine() : this(null, null) + { + } - /// - /// Constructs a new engine instance and allows customizing options. - /// - public Engine(Action? options) - : this(null, options != null ? (_, opts) => options.Invoke(opts) : null) - { - } + /// + /// Constructs a new engine instance and allows customizing options. + /// + public Engine(Action? options) + : this(null, options != null ? (_, opts) => options.Invoke(opts) : null) + { + } - /// - /// Constructs a new engine with a custom instance. - /// - public Engine(Options options) : this(options, null) - { - } + /// + /// Constructs a new engine with a custom instance. + /// + public Engine(Options options) : this(options, null) + { + } - /// - /// Constructs a new engine instance and allows customizing options. - /// - /// The provided engine instance in callback is not guaranteed to be fully configured - public Engine(Action options) : this(null, options) - { - } + /// + /// Constructs a new engine instance and allows customizing options. + /// + /// The provided engine instance in callback is not guaranteed to be fully configured + public Engine(Action options) : this(null, options) + { + } - private Engine(Options? options, Action? configure) - { - Advanced = new AdvancedOperations(this); - Constraints = new ConstraintOperations(this); - TypeConverter = new DefaultTypeConverter(this); + private Engine(Options? options, Action? configure) + { + Advanced = new AdvancedOperations(this); + Constraints = new ConstraintOperations(this); + TypeConverter = new DefaultTypeConverter(this); - _executionContexts = new ExecutionContextStack(2); + _executionContexts = new ExecutionContextStack(2); - // we can use default options if there's no action to modify it - Options = options ?? (configure is not null ? new Options() : _defaultEngineOptions); + // we can use default options if there's no action to modify it + Options = options ?? (configure is not null ? new Options() : _defaultEngineOptions); - configure?.Invoke(this, Options); + configure?.Invoke(this, Options); - _extensionMethods = ExtensionMethodCache.Build(Options.Interop.ExtensionMethodTypes); + _extensionMethods = ExtensionMethodCache.Build(Options.Interop.ExtensionMethodTypes); - Reset(); + Reset(); - // gather some options as fields for faster checks - _isDebugMode = Options.Debugger.Enabled; - _isStrict = Options.Strict; + // gather some options as fields for faster checks + _isDebugMode = Options.Debugger.Enabled; + _isStrict = Options.Strict; - _objectConverters = Options.Interop.ObjectConverters.Count > 0 - ? Options.Interop.ObjectConverters.ToArray() - : null; + _objectConverters = Options.Interop.ObjectConverters.Count > 0 + ? Options.Interop.ObjectConverters.ToArray() + : null; - _constraints = Options.Constraints.Constraints.ToArray(); - _referenceResolver = Options.ReferenceResolver; + _constraints = Options.Constraints.Constraints.ToArray(); + _referenceResolver = Options.ReferenceResolver; - _referencePool = new ReferencePool(); - _argumentsInstancePool = new ArgumentsInstancePool(this); - _jsValueArrayPool = new JsValueArrayPool(); + _referencePool = new ReferencePool(); + _argumentsInstancePool = new ArgumentsInstancePool(this); + _jsValueArrayPool = new JsValueArrayPool(); - Options.Apply(this); + Options.Apply(this); - CallStack = new JintCallStack(Options.Constraints.MaxRecursionDepth >= 0); - _stackGuard = new StackGuard(this); + CallStack = new JintCallStack(Options.Constraints.MaxRecursionDepth >= 0); + _stackGuard = new StackGuard(this); - var defaultParserOptions = ScriptParsingOptions.Default.GetParserOptions(Options); - _defaultParser = new Parser(defaultParserOptions); - } + var defaultParserOptions = ScriptParsingOptions.Default.GetParserOptions(Options); + _defaultParser = new Parser(defaultParserOptions); + } - private void Reset() - { - _host = Options.Host.Factory(this); - _host.Initialize(this); - } + private void Reset() + { + _host = Options.Host.Factory(this); + _host.Initialize(this); + } - internal ref readonly ExecutionContext ExecutionContext - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref _executionContexts.Peek(); - } + internal ref readonly ExecutionContext ExecutionContext + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref _executionContexts.Peek(); + } - // temporary state for realm so that we can easily pass it to functions while still not - // having a proper execution context established - internal Realm? _realmInConstruction; + // temporary state for realm so that we can easily pass it to functions while still not + // having a proper execution context established + internal Realm? _realmInConstruction; - internal Node? _lastSyntaxElement; + internal Node? _lastSyntaxElement; - internal Realm Realm => _realmInConstruction ?? ExecutionContext.Realm; + internal Realm Realm => _realmInConstruction ?? ExecutionContext.Realm; - /// - /// The well-known intrinsics for this engine instance. - /// - public Intrinsics Intrinsics => Realm.Intrinsics; + /// + /// The well-known intrinsics for this engine instance. + /// + public Intrinsics Intrinsics => Realm.Intrinsics; - /// - /// The global object for this engine instance. - /// - public ObjectInstance Global => Realm.GlobalObject; + /// + /// The global object for this engine instance. + /// + public ObjectInstance Global => Realm.GlobalObject; - internal GlobalSymbolRegistry GlobalSymbolRegistry { get; } = new(); + internal GlobalSymbolRegistry GlobalSymbolRegistry { get; } = new(); - internal long CurrentMemoryUsage { get; private set; } + internal long CurrentMemoryUsage { get; private set; } - internal Options Options - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get; - private set; - } + internal Options Options + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + private set; + } - public DebugHandler Debugger => _debugger ??= new DebugHandler(this, Options.Debugger.InitialStepMode); + public DebugHandler Debugger => _debugger ??= new DebugHandler(this, Options.Debugger.InitialStepMode); - internal ParserOptions DefaultModuleParserOptions => _defaultModuleParserOptions ??= ModuleParsingOptions.Default.GetParserOptions(Options); + internal ParserOptions DefaultModuleParserOptions => _defaultModuleParserOptions ??= ModuleParsingOptions.Default.GetParserOptions(Options); - internal ParserOptions GetActiveParserOptions() - { - return _executionContexts?.GetActiveParserOptions() ?? _defaultParser.Options; - } + internal ParserOptions GetActiveParserOptions() + { + return _executionContexts?.GetActiveParserOptions() ?? _defaultParser.Options; + } - internal Parser GetParserFor(ScriptParsingOptions parsingOptions) - { - return ReferenceEquals(parsingOptions, ScriptParsingOptions.Default) - ? _defaultParser - : new Parser(parsingOptions.GetParserOptions(Options)); - } + internal Parser GetParserFor(ScriptParsingOptions parsingOptions) + { + return ReferenceEquals(parsingOptions, ScriptParsingOptions.Default) + ? _defaultParser + : new Parser(parsingOptions.GetParserOptions(Options)); + } - internal Parser GetParserFor(ParserOptions parserOptions) - { - return ReferenceEquals(parserOptions, _defaultParser.Options) ? _defaultParser : new Parser(parserOptions); - } + internal Parser GetParserFor(ParserOptions parserOptions) + { + return ReferenceEquals(parserOptions, _defaultParser.Options) ? _defaultParser : new Parser(parserOptions); + } - internal void EnterExecutionContext( - Environment lexicalEnvironment, - Environment variableEnvironment, - Realm realm, - PrivateEnvironment? privateEnvironment) - { - var context = new ExecutionContext( - null, - lexicalEnvironment, - variableEnvironment, - privateEnvironment, - realm, - null); - - _executionContexts.Push(context); - } + internal void EnterExecutionContext( + Environment lexicalEnvironment, + Environment variableEnvironment, + Realm realm, + PrivateEnvironment? privateEnvironment) + { + var context = new ExecutionContext( + null, + lexicalEnvironment, + variableEnvironment, + privateEnvironment, + realm, + null); + + _executionContexts.Push(context); + } - internal void EnterExecutionContext(in ExecutionContext context) - { - _executionContexts.Push(context); - } + internal void EnterExecutionContext(in ExecutionContext context) + { + _executionContexts.Push(context); + } - /// - /// Registers a delegate with given name. Delegate becomes a JavaScript function that can be called. - /// - public Engine SetValue(string name, Delegate value) - { - Realm.GlobalObject.FastSetProperty(name, new PropertyDescriptor(new DelegateWrapper(this, value), PropertyFlag.NonEnumerable)); - return this; - } + /// + /// Registers a delegate with given name. Delegate becomes a JavaScript function that can be called. + /// + public Engine SetValue(string name, Delegate value) + { + Realm.GlobalObject.FastSetProperty(name, new PropertyDescriptor(new DelegateWrapper(this, value), PropertyFlag.NonEnumerable)); + return this; + } - /// - /// Registers a string value as variable. - /// - public Engine SetValue(string name, string? value) - { - return SetValue(name, value is null ? JsValue.Null : JsString.Create(value)); - } + /// + /// Registers a string value as variable. + /// + public Engine SetValue(string name, string? value) + { + return SetValue(name, value is null ? JsValue.Null : JsString.Create(value)); + } - /// - /// Registers a double value as variable. - /// - public Engine SetValue(string name, double value) - { - return SetValue(name, (JsValue) JsNumber.Create(value)); - } + /// + /// Registers a double value as variable. + /// + public Engine SetValue(string name, double value) + { + return SetValue(name, (JsValue) JsNumber.Create(value)); + } - /// - /// Registers an integer value as variable. - /// - public Engine SetValue(string name, int value) - { - return SetValue(name, (JsValue) JsNumber.Create(value)); - } + /// + /// Registers an integer value as variable. + /// + public Engine SetValue(string name, int value) + { + return SetValue(name, (JsValue) JsNumber.Create(value)); + } - /// - /// Registers a boolean value as variable. - /// - public Engine SetValue(string name, bool value) - { - return SetValue(name, (JsValue) (value ? JsBoolean.True : JsBoolean.False)); - } + /// + /// Registers a boolean value as variable. + /// + public Engine SetValue(string name, bool value) + { + return SetValue(name, (JsValue) (value ? JsBoolean.True : JsBoolean.False)); + } - /// - /// Registers a native JS value as variable. - /// - public Engine SetValue(string name, JsValue value) - { - Realm.GlobalObject.Set(name, value); - return this; - } + /// + /// Registers a native JS value as variable. + /// + public Engine SetValue(string name, JsValue value) + { + Realm.GlobalObject.Set(name, value); + return this; + } - /// - /// Registers an object value as variable, creates an interop wrapper when needed. - /// - public Engine SetValue(string name, object? obj) - { - var value = obj is Type t - ? TypeReference.CreateTypeReference(this, t) - : JsValue.FromObject(this, obj); + /// + /// Registers an object value as variable, creates an interop wrapper when needed. + /// + public Engine SetValue(string name, object? obj) + { + var value = obj is Type t + ? TypeReference.CreateTypeReference(this, t) + : JsValue.FromObject(this, obj); - return SetValue(name, value); - } + return SetValue(name, value); + } - /// - /// Registers an object value as variable, creates an interop wrapper when needed. - /// - public Engine SetValue(string name, [DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] Type type) - { + /// + /// Registers an object value as variable, creates an interop wrapper when needed. + /// + public Engine SetValue(string name, [DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] Type type) + { #pragma warning disable IL2111 - return SetValue(name, TypeReference.CreateTypeReference(this, type)); + return SetValue(name, TypeReference.CreateTypeReference(this, type)); #pragma warning restore IL2111 - } + } - /// - /// Registers an object value as variable, creates an interop wrapper when needed. - /// - public Engine SetValue<[DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] T>(string name, T? obj) - { - return obj is Type t - ? SetValue(name, t) - : SetValue(name, JsValue.FromObject(this, obj)); - } + /// + /// Registers an object value as variable, creates an interop wrapper when needed. + /// + public Engine SetValue<[DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] T>(string name, T? obj) + { + return obj is Type t + ? SetValue(name, t) + : SetValue(name, JsValue.FromObject(this, obj)); + } - internal void LeaveExecutionContext() - { - _executionContexts.Pop(); - } + internal void LeaveExecutionContext() + { + _executionContexts.Pop(); + } - internal void ResetConstraints() + internal void ResetConstraints() + { + foreach (var constraint in _constraints) { - foreach (var constraint in _constraints) - { - constraint.Reset(); - } + constraint.Reset(); } + } - /// - /// Initializes list of references of called functions - /// - internal void ResetCallStack() - { - CallStack.Clear(); - } + /// + /// Initializes list of references of called functions + /// + internal void ResetCallStack() + { + CallStack.Clear(); + } - /// - /// Evaluates code and returns last return value. - /// - public JsValue Evaluate(string code, string? source = null) - { - var script = _defaultParser.ParseScriptGuarded(Realm, code, source ?? "", _isStrict); - return Evaluate(new Prepared