Browse Source

Support for JSON.parse reviver (#484)

Fixes #436
Pranay Kumar 7 years ago
parent
commit
c364bf9a4b
2 changed files with 109 additions and 1 deletions
  1. 23 0
      Jint.Tests/Runtime/EngineTests.cs
  2. 86 1
      Jint/Native/Json/JsonInstance.cs

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

@@ -1016,6 +1016,29 @@ namespace Jint.Tests.Runtime
             ");
         }
 
+        [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(@"
+                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){
+                    actualKeys.push(key);
+                    return value;// return the unchanged property value.
+                });
+                expectedKeys.forEach(function (val, i){
+                    assert(actualKeys[i] === val);
+                });
+            ");
+        }
+
         [Fact]
         [ReplaceCulture("fr-FR")]
         public void ShouldBeCultureInvariant()

+ 86 - 1
Jint/Native/Json/JsonInstance.cs

@@ -1,5 +1,6 @@
 using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native.Json
@@ -7,6 +8,7 @@ namespace Jint.Native.Json
     public sealed class JsonInstance : ObjectInstance
     {
         private readonly Engine _engine;
+        private JsValue _reviver;
 
         private JsonInstance(Engine engine)
             : base(engine)
@@ -36,10 +38,93 @@ namespace Jint.Native.Json
             FastAddProperty("stringify", new ClrFunctionInstance(Engine, Stringify, 3), true, false, true);
         }
 
+        private JsValue AbstractWalkOperation(ObjectInstance thisObject, string prop)
+        {
+            JsValue value = thisObject.Get(prop);
+            if (value.IsObject())
+            {
+                var valueAsObject = value.AsObject();
+                if (valueAsObject.Class == "Array")
+                {
+                    var valAsArray = value.AsArray();
+                    var i = 0;
+                    var arrLen = valAsArray.GetLength();
+                    while (i < arrLen)
+                    {
+                        var newValue = AbstractWalkOperation(valAsArray, TypeConverter.ToString(i));
+                        if (newValue.IsUndefined())
+                        {
+                            valAsArray.Delete(TypeConverter.ToString(i), false);
+                        }
+                        else
+                        {
+                            valAsArray.DefineOwnProperty
+                            (
+                                TypeConverter.ToString(i),
+                                new PropertyDescriptor
+                                (
+                                    value: newValue,
+                                    writable: true,
+                                    enumerable: true,
+                                    configurable: true
+                                ),
+                                false
+                            );
+                        }
+                        i = i + 1;
+                    }
+                }
+                else
+                {
+                    var keys = valueAsObject.GetOwnProperties();
+                    foreach (var p in keys)
+                    {
+                        var newElement = AbstractWalkOperation(valueAsObject, p.Key);
+                        if (newElement.IsUndefined())
+                        {
+                            valueAsObject.Delete(p.Key, false);
+                        }
+                        else
+                        {
+                            valueAsObject.DefineOwnProperty(
+                                p.Key,
+                                new PropertyDescriptor
+                                (
+                                    value: newElement,
+                                    writable: true,
+                                    enumerable: true,
+                                    configurable: true
+                                ),
+                                false
+                            );
+                        }
+                    }
+                }
+            }
+            return _reviver.Invoke(thisObject, new JsValue[] { prop, value });
+        }
+
         public JsValue Parse(JsValue thisObject, JsValue[] arguments)
         {
             var parser = new JsonParser(_engine);
-            return parser.Parse(TypeConverter.ToString(arguments[0]));
+            var res = parser.Parse(TypeConverter.ToString(arguments[0]));
+            if (arguments.Length > 1)
+            {
+                this._reviver = arguments[1];
+                ObjectInstance revRes = ObjectConstructor.CreateObjectConstructor(_engine).Construct(Arguments.Empty);
+                revRes.DefineOwnProperty(
+                    "",
+                    new PropertyDescriptor(
+                        value: res,
+                        writable: true,
+                        enumerable: true,
+                        configurable: true
+                    ),
+                    false
+                );
+                return AbstractWalkOperation(revRes, "");
+            }
+            return res;
         }
 
         public JsValue Stringify(JsValue thisObject, JsValue[] arguments)