Browse Source

Add example IObjectConverter for System.Text.Json interop (#1803)

Tony Han 1 year ago
parent
commit
afdc08291e

+ 82 - 20
Jint.Tests.PublicInterface/InteropTests.SystemTextJson.cs

@@ -2,9 +2,59 @@ using System.Reflection;
 using System.Text.Json.Nodes;
 using System.Text.Json.Nodes;
 using Jint.Native;
 using Jint.Native;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interop;
-
+using System.Text.Json;
 namespace Jint.Tests.PublicInterface;
 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<double>(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
 public partial class InteropTests
 {
 {
     [Fact]
     [Fact]
@@ -12,9 +62,14 @@ public partial class InteropTests
     {
     {
         const string Json = """
         const string Json = """
         {
         {
+            "falseValue": false,
             "employees": {
             "employees": {
-                "boolean": true,
+                "trueValue": true,
+                "falseValue": false,
                 "number": 123.456,
                 "number": 123.456,
+                "zeroNumber": 0,
+                "emptyString":"",
+                "nullValue":null,
                 "other": "abc",
                 "other": "abc",
                 "type": "array",
                 "type": "array",
                 "value": [
                 "value": [
@@ -45,24 +100,10 @@ public partial class InteropTests
                     return wrapped;
                     return wrapped;
                 }
                 }
 
 
-                if (target is JsonValue jsonValue)
-                {
-                    if (jsonValue.TryGetValue<bool>(out var boolValue))
-                    {
-                        return e.Construct("Boolean", boolValue ? JsBoolean.True : JsBoolean.False);
-                    }
-
-                    if (jsonValue.TryGetValue<double>(out var doubleValue))
-                    {
-                        return e.Construct("Number", JsNumber.Create(doubleValue));
-                    }
-
-                    return e.Construct("String", (JsString)jsonValue.ToString());
-                }
-
                 return new ObjectWrapper(e, target);
                 return new ObjectWrapper(e, target);
             };
             };
 
 
+            options.AddObjectConverter(new TestJsonValueConverter());
             // we cannot access this[string] with anything else than JsonObject, otherwise itw will throw
             // we cannot access this[string] with anything else than JsonObject, otherwise itw will throw
             options.Interop.TypeResolver = new TypeResolver
             options.Interop.TypeResolver = new TypeResolver
             {
             {
@@ -80,6 +121,7 @@ public partial class InteropTests
         });
         });
 
 
         engine
         engine
+            .SetValue("falseValue", false)
             .SetValue("variables", variables)
             .SetValue("variables", variables)
             .Execute("""
             .Execute("""
                  function populateFullName() {
                  function populateFullName() {
@@ -101,7 +143,7 @@ public partial class InteropTests
         Assert.Equal((uint) 2, result.Length);
         Assert.Equal((uint) 2, result.Length);
         Assert.Equal("John Doe", result[0].AsObject()["fullName"]);
         Assert.Equal("John Doe", result[0].AsObject()["fullName"]);
         Assert.Equal("Jane Doe", result[1].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.number == 123.456").AsBoolean());
         Assert.True(engine.Evaluate("variables.employees.other == 'abc'").AsBoolean());
         Assert.True(engine.Evaluate("variables.employees.other == 'abc'").AsBoolean());
 
 
@@ -115,8 +157,27 @@ public partial class InteropTests
         Assert.Equal((uint) 2, result.Length);
         Assert.Equal((uint) 2, result.Length);
         Assert.Equal("Jake Doe", result[0].AsObject()["fullName"]);
         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
         // mutating original object that is wrapped inside the engine
-        variables["employees"]["boolean"] = false;
+        variables["employees"]["trueValue"] = false;
         variables["employees"]["number"] = 456.789;
         variables["employees"]["number"] = 456.789;
         variables["employees"]["other"] = "def";
         variables["employees"]["other"] = "def";
         variables["employees"]["type"] = "array";
         variables["employees"]["type"] = "array";
@@ -127,8 +188,9 @@ public partial class InteropTests
         result = engine.Evaluate("populateFullName()").AsArray();
         result = engine.Evaluate("populateFullName()").AsArray();
         Assert.Equal((uint) 2, result.Length);
         Assert.Equal((uint) 2, result.Length);
         Assert.Equal("John Doe", result[0].AsObject()["fullName"]);
         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.number == 456.789").AsBoolean());
         Assert.True(engine.Evaluate("variables.employees.other == 'def'").AsBoolean());
         Assert.True(engine.Evaluate("variables.employees.other == 'def'").AsBoolean());
     }
     }
 }
 }
+#endif

+ 4 - 4
Jint/Native/Array/ArrayPrototype.cs

@@ -925,7 +925,7 @@ namespace Jint.Native.Array
             {
             {
                 insertCount = (ulong) (arguments.Length - 2);
                 insertCount = (ulong) (arguments.Length - 2);
                 var dc = TypeConverter.ToInteger(deleteCount);
                 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<JsValue>();
                 items = System.Array.Empty<JsValue>();
                 if (arguments.Length > 2)
                 if (arguments.Length > 2)
@@ -982,7 +982,7 @@ namespace Jint.Native.Array
                 for (var k = len - actualDeleteCount; k > actualStart; k--)
                 for (var k = len - actualDeleteCount; k > actualStart; k--)
                 {
                 {
                     var from = k + actualDeleteCount - 1;
                     var from = k + actualDeleteCount - 1;
-                    var to =  k + (ulong) items.Length - 1;
+                    var to = k + (ulong) items.Length - 1;
                     if (o.HasProperty(from))
                     if (o.HasProperty(from))
                     {
                     {
                         var fromValue = o.Get(from);
                         var fromValue = o.Get(from);
@@ -1320,7 +1320,7 @@ namespace Jint.Native.Array
         private JsValue Concat(JsValue thisObject, JsValue[] arguments)
         private JsValue Concat(JsValue thisObject, JsValue[] arguments)
         {
         {
             var o = TypeConverter.ToObject(_realm, thisObject);
             var o = TypeConverter.ToObject(_realm, thisObject);
-            var items = new List<JsValue>(arguments.Length + 1) {o};
+            var items = new List<JsValue>(arguments.Length + 1) { o };
             items.AddRange(arguments);
             items.AddRange(arguments);
 
 
             uint n = 0;
             uint n = 0;
@@ -1466,7 +1466,7 @@ namespace Jint.Native.Array
             {
             {
                 insertCount = (ulong) (arguments.Length - 2);
                 insertCount = (ulong) (arguments.Length - 2);
                 var dc = TypeConverter.ToIntegerOrInfinity(deleteCount);
                 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<JsValue>();
                 items = System.Array.Empty<JsValue>();
                 if (arguments.Length > 2)
                 if (arguments.Length > 2)