diff --git a/Jint.Tests.PublicInterface/InteropTests.SystemTextJson.cs b/Jint.Tests.PublicInterface/InteropTests.SystemTextJson.cs index 3b178ac3d5..ac97e4cf50 100644 --- a/Jint.Tests.PublicInterface/InteropTests.SystemTextJson.cs +++ b/Jint.Tests.PublicInterface/InteropTests.SystemTextJson.cs @@ -2,9 +2,59 @@ using System.Text.Json.Nodes; using Jint.Native; using Jint.Runtime.Interop; - +using System.Text.Json; namespace Jint.Tests.PublicInterface; +#if NET8_0_OR_GREATER +public class TestJsonValueConverter : IObjectConverter +{ + public bool TryConvert(Engine engine, object value, out JsValue result) + { + if (value is JsonValue jsonValue) + { + var valueKind = jsonValue.GetValueKind(); + switch (valueKind) + { + case JsonValueKind.Object: + case JsonValueKind.Array: + result = JsValue.FromObject(engine, jsonValue); + break; + case JsonValueKind.String: + result = jsonValue.ToString(); + break; + case JsonValueKind.Number: + if (jsonValue.TryGetValue(out var doubleValue)) + { + result = JsNumber.Create(doubleValue); + } + else + { + result = JsValue.Undefined; + } + break; + case JsonValueKind.True: + result = JsBoolean.True; + break; + case JsonValueKind.False: + result = JsBoolean.False; + break; + case JsonValueKind.Undefined: + result = JsValue.Undefined; + break; + case JsonValueKind.Null: + result = JsValue.Null; + break; + default: + result = JsValue.Undefined; + break; + } + return true; + } + result = JsValue.Undefined; + return false; + + } +} public partial class InteropTests { [Fact] @@ -12,9 +62,14 @@ public void AccessingJsonNodeShouldWork() { const string Json = """ { + "falseValue": false, "employees": { - "boolean": true, + "trueValue": true, + "falseValue": false, "number": 123.456, + "zeroNumber": 0, + "emptyString":"", + "nullValue":null, "other": "abc", "type": "array", "value": [ @@ -45,24 +100,10 @@ public void AccessingJsonNodeShouldWork() return wrapped; } - if (target is JsonValue jsonValue) - { - if (jsonValue.TryGetValue(out var boolValue)) - { - return e.Construct("Boolean", boolValue ? JsBoolean.True : JsBoolean.False); - } - - if (jsonValue.TryGetValue(out var doubleValue)) - { - return e.Construct("Number", JsNumber.Create(doubleValue)); - } - - return e.Construct("String", (JsString)jsonValue.ToString()); - } - return new ObjectWrapper(e, target); }; + options.AddObjectConverter(new TestJsonValueConverter()); // we cannot access this[string] with anything else than JsonObject, otherwise itw will throw options.Interop.TypeResolver = new TypeResolver { @@ -80,6 +121,7 @@ public void AccessingJsonNodeShouldWork() }); engine + .SetValue("falseValue", false) .SetValue("variables", variables) .Execute(""" function populateFullName() { @@ -101,7 +143,7 @@ function populateFullName() { Assert.Equal((uint) 2, result.Length); Assert.Equal("John Doe", result[0].AsObject()["fullName"]); Assert.Equal("Jane Doe", result[1].AsObject()["fullName"]); - Assert.True(engine.Evaluate("variables.employees.boolean == true").AsBoolean()); + Assert.True(engine.Evaluate("variables.employees.trueValue == true").AsBoolean()); Assert.True(engine.Evaluate("variables.employees.number == 123.456").AsBoolean()); Assert.True(engine.Evaluate("variables.employees.other == 'abc'").AsBoolean()); @@ -115,8 +157,27 @@ function populateFullName() { Assert.Equal((uint) 2, result.Length); Assert.Equal("Jake Doe", result[0].AsObject()["fullName"]); + // Validate boolean value in the if condition. + Assert.Equal(1, engine.Evaluate("if(!falseValue){ return 1 ;} else {return 0;}").AsNumber()); + Assert.Equal(1, engine.Evaluate("if(falseValue===false){ return 1 ;} else {return 0;}").AsNumber()); + Assert.True(engine.Evaluate("!variables.zeroNumber").AsBoolean()); + Assert.True(engine.Evaluate("!variables.emptyString").AsBoolean()); + Assert.True(engine.Evaluate("!variables.nullValue").AsBoolean()); + var result2 = engine.Evaluate("!variables.falseValue"); + var result3 = engine.Evaluate("!falseValue"); + var result4 = engine.Evaluate("variables.falseValue"); + var result5 = engine.Evaluate("falseValue"); + Assert.NotNull(result2); + + Assert.Equal(1, engine.Evaluate("if(variables.falseValue===false){ return 1 ;} else {return 0;}").AsNumber()); + Assert.Equal(1, engine.Evaluate("if(falseValue===variables.falseValue){ return 1 ;} else {return 0;}").AsNumber()); + Assert.Equal(1, engine.Evaluate("if(!variables.falseValue){ return 1 ;} else {return 0;}").AsNumber()); + Assert.Equal(1, engine.Evaluate("if(!variables.employees.falseValue){ return 1 ;} else {return 0;}").AsNumber()); + Assert.Equal(0, engine.Evaluate("if(!variables.employees.trueValue) return 1 ; else return 0;").AsNumber()); + + // mutating original object that is wrapped inside the engine - variables["employees"]["boolean"] = false; + variables["employees"]["trueValue"] = false; variables["employees"]["number"] = 456.789; variables["employees"]["other"] = "def"; variables["employees"]["type"] = "array"; @@ -127,8 +188,9 @@ function populateFullName() { result = engine.Evaluate("populateFullName()").AsArray(); Assert.Equal((uint) 2, result.Length); Assert.Equal("John Doe", result[0].AsObject()["fullName"]); - Assert.True(engine.Evaluate("variables.employees.boolean == false").AsBoolean()); + Assert.True(engine.Evaluate("variables.employees.trueValue == false").AsBoolean()); Assert.True(engine.Evaluate("variables.employees.number == 456.789").AsBoolean()); Assert.True(engine.Evaluate("variables.employees.other == 'def'").AsBoolean()); } } +#endif diff --git a/Jint/Native/Array/ArrayPrototype.cs b/Jint/Native/Array/ArrayPrototype.cs index 527e908514..d386950eaa 100644 --- a/Jint/Native/Array/ArrayPrototype.cs +++ b/Jint/Native/Array/ArrayPrototype.cs @@ -925,7 +925,7 @@ private JsValue Splice(JsValue thisObject, JsValue[] arguments) { insertCount = (ulong) (arguments.Length - 2); var dc = TypeConverter.ToInteger(deleteCount); - actualDeleteCount = (ulong) System.Math.Min(System.Math.Max(dc,0), len - actualStart); + actualDeleteCount = (ulong) System.Math.Min(System.Math.Max(dc, 0), len - actualStart); items = System.Array.Empty(); if (arguments.Length > 2) @@ -982,7 +982,7 @@ private JsValue Splice(JsValue thisObject, JsValue[] arguments) for (var k = len - actualDeleteCount; k > actualStart; k--) { var from = k + actualDeleteCount - 1; - var to = k + (ulong) items.Length - 1; + var to = k + (ulong) items.Length - 1; if (o.HasProperty(from)) { var fromValue = o.Get(from); @@ -1320,7 +1320,7 @@ private JsValue ToLocaleString(JsValue thisObject, JsValue[] arguments) private JsValue Concat(JsValue thisObject, JsValue[] arguments) { var o = TypeConverter.ToObject(_realm, thisObject); - var items = new List(arguments.Length + 1) {o}; + var items = new List(arguments.Length + 1) { o }; items.AddRange(arguments); uint n = 0; @@ -1466,7 +1466,7 @@ private JsValue ToSpliced(JsValue thisObject, JsValue[] arguments) { insertCount = (ulong) (arguments.Length - 2); var dc = TypeConverter.ToIntegerOrInfinity(deleteCount); - actualDeleteCount = (ulong) System.Math.Min(System.Math.Max(dc,0), len - actualStart); + actualDeleteCount = (ulong) System.Math.Min(System.Math.Max(dc, 0), len - actualStart); items = System.Array.Empty(); if (arguments.Length > 2)