using Jint.Collections; using Jint.Native.Object; using Jint.Native.Symbol; using Jint.Runtime; using Jint.Runtime.Descriptors; using Jint.Runtime.Interop; namespace Jint.Native.Json { internal sealed class JsonInstance : ObjectInstance { private readonly Realm _realm; internal JsonInstance( Engine engine, Realm realm, ObjectPrototype objectPrototype) : base(engine) { _realm = realm; _prototype = objectPrototype; } protected override void Initialize() { var properties = new PropertyDictionary(2, checkExistingKeys: false) { #pragma warning disable 618 ["parse"] = new PropertyDescriptor(new ClrFunction(Engine, "parse", Parse, 2, PropertyFlag.Configurable), true, false, true), ["stringify"] = new PropertyDescriptor(new ClrFunction(Engine, "stringify", Stringify, 3, PropertyFlag.Configurable), true, false, true) #pragma warning restore 618 }; SetProperties(properties); var symbols = new SymbolDictionary(1) { [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("JSON", false, false, true), }; SetSymbols(symbols); } private static JsValue InternalizeJSONProperty(JsValue holder, JsValue name, ICallable reviver) { var temp = holder.Get(name); if (temp is ObjectInstance val) { if (val.IsArray()) { var i = 0UL; var len = TypeConverter.ToLength(val.Get(CommonProperties.Length)); while (i < len) { var prop = JsString.Create(i); var newElement = InternalizeJSONProperty(val, prop, reviver); if (newElement.IsUndefined()) { val.Delete(prop); } else { val.CreateDataProperty(prop, newElement); } i = i + 1; } } else { var keys = val.EnumerableOwnProperties(EnumerableOwnPropertyNamesKind.Key); foreach (var p in keys) { var newElement = InternalizeJSONProperty(val, p, reviver); if (newElement.IsUndefined()) { val.Delete(p); } else { val.CreateDataProperty(p, newElement); } } } } return reviver.Call(holder, new[] { name, temp }); } /// /// https://tc39.es/ecma262/#sec-json.parse /// private JsValue Parse(JsValue thisObject, JsValue[] arguments) { var jsonString = TypeConverter.ToString(arguments.At(0)); var reviver = arguments.At(1); var parser = new JsonParser(_engine); var unfiltered = parser.Parse(jsonString); if (reviver.IsCallable) { var root = _realm.Intrinsics.Object.Construct(Arguments.Empty); var rootName = JsString.Empty; root.CreateDataPropertyOrThrow(rootName, unfiltered); return InternalizeJSONProperty(root, rootName, (ICallable) reviver); } else { return unfiltered; } } private JsValue Stringify(JsValue thisObject, JsValue[] arguments) { var value = arguments.At(0); var replacer = arguments.At(1); var space = arguments.At(2); if (value.IsUndefined() && replacer.IsUndefined()) { return Undefined; } var serializer = new JsonSerializer(_engine); return serializer.Serialize(value, replacer, space); } } }