diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 49a5a78f4b..c719acde11 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -278,48 +278,105 @@ public static byte[] MakeScript(this UInt160 scriptHash, string method, params o /// Converts the to a JSON object. /// /// The to convert. + /// The maximum size in bytes of the result. /// The represented by a JSON object. - public static JObject ToJson(this StackItem item) + public static JObject ToJson(this StackItem item, int maxSize = int.MaxValue) { - return ToJson(item, null); + return ToJson(item, null, ref maxSize); } - private static JObject ToJson(StackItem item, HashSet context) + /// + /// Converts the to a JSON object. + /// + /// The to convert. + /// The maximum size in bytes of the result. + /// The represented by a JSON object. + public static JArray ToJson(this EvaluationStack stack, int maxSize = int.MaxValue) + { + if (maxSize <= 0) throw new ArgumentOutOfRangeException(nameof(maxSize)); + maxSize -= 2/*[]*/+ Math.Max(0, (stack.Count - 1))/*,*/; + JArray result = new(); + foreach (var item in stack) + result.Add(ToJson(item, null, ref maxSize)); + if (maxSize < 0) throw new InvalidOperationException("Max size reached."); + return result; + } + + private static JObject ToJson(StackItem item, HashSet context, ref int maxSize) { - JObject json = new(); - json["type"] = item.Type; + JObject json = new() + { + ["type"] = item.Type + }; + JObject value = null; + maxSize -= 11/*{"type":""}*/+ item.Type.ToString().Length; switch (item) { case Array array: - context ??= new HashSet(ReferenceEqualityComparer.Instance); - if (!context.Add(array)) throw new InvalidOperationException(); - json["value"] = new JArray(array.Select(p => ToJson(p, context))); - break; + { + context ??= new HashSet(ReferenceEqualityComparer.Instance); + if (!context.Add(array)) throw new InvalidOperationException(); + maxSize -= 2/*[]*/+ Math.Max(0, (array.Count - 1))/*,*/; + JArray a = new(); + foreach (StackItem stackItem in array) + a.Add(ToJson(stackItem, context, ref maxSize)); + value = a; + break; + } case Boolean boolean: - json["value"] = boolean.GetBoolean(); - break; + { + bool b = boolean.GetBoolean(); + maxSize -= b ? 4/*true*/: 5/*false*/; + value = b; + break; + } case Buffer _: case ByteString _: - json["value"] = Convert.ToBase64String(item.GetSpan()); - break; + { + string s = Convert.ToBase64String(item.GetSpan()); + maxSize -= 2/*""*/+ s.Length; + value = s; + break; + } case Integer integer: - json["value"] = integer.GetInteger().ToString(); - break; + { + string s = integer.GetInteger().ToString(); + maxSize -= 2/*""*/+ s.Length; + value = s; + break; + } case Map map: - context ??= new HashSet(ReferenceEqualityComparer.Instance); - if (!context.Add(map)) throw new InvalidOperationException(); - json["value"] = new JArray(map.Select(p => { - JObject item = new(); - item["key"] = ToJson(p.Key, context); - item["value"] = ToJson(p.Value, context); - return item; - })); - break; + context ??= new HashSet(ReferenceEqualityComparer.Instance); + if (!context.Add(map)) throw new InvalidOperationException(); + maxSize -= 2/*[]*/+ Math.Max(0, (map.Count - 1))/*,*/; + JArray a = new(); + foreach (var (k, v) in map) + { + maxSize -= 17/*{"key":,"value":}*/; + JObject i = new() + { + ["key"] = ToJson(k, context, ref maxSize), + ["value"] = ToJson(v, context, ref maxSize) + }; + a.Add(i); + } + value = a; + break; + } case Pointer pointer: - json["value"] = pointer.Position; - break; + { + maxSize -= pointer.Position.ToString().Length; + value = pointer.Position; + break; + } + } + if (value is not null) + { + maxSize -= 9/*,"value":*/; + json["value"] = value; } + if (maxSize < 0) throw new InvalidOperationException("Max size reached."); return json; }