Skip to content

Commit

Permalink
Add example IObjectConverter for System.Text.Json interop (#1803)
Browse files Browse the repository at this point in the history
  • Loading branch information
hyzx86 authored Mar 14, 2024
1 parent 9679288 commit afdc082
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 24 deletions.
102 changes: 82 additions & 20 deletions Jint.Tests.PublicInterface/InteropTests.SystemTextJson.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,74 @@
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<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
{
[Fact]
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": [
Expand Down Expand Up @@ -45,24 +100,10 @@ public void AccessingJsonNodeShouldWork()
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);
};
options.AddObjectConverter(new TestJsonValueConverter());
// we cannot access this[string] with anything else than JsonObject, otherwise itw will throw
options.Interop.TypeResolver = new TypeResolver
{
Expand All @@ -80,6 +121,7 @@ public void AccessingJsonNodeShouldWork()
});

engine
.SetValue("falseValue", false)
.SetValue("variables", variables)
.Execute("""
function populateFullName() {
Expand All @@ -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());

Expand All @@ -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";
Expand All @@ -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
8 changes: 4 additions & 4 deletions Jint/Native/Array/ArrayPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<JsValue>();
if (arguments.Length > 2)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<JsValue>(arguments.Length + 1) {o};
var items = new List<JsValue>(arguments.Length + 1) { o };
items.AddRange(arguments);

uint n = 0;
Expand Down Expand Up @@ -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<JsValue>();
if (arguments.Length > 2)
Expand Down

0 comments on commit afdc082

Please sign in to comment.