From c129b3d21ebd48973a1e5656fcb56efc657d29fa Mon Sep 17 00:00:00 2001 From: Rob Rolnick Date: Wed, 17 Feb 2021 09:49:01 -0800 Subject: [PATCH] Fix crash when deserializing from Stream into SourceMap (#85) * Json.net is throwing an exception when trying to deserialize into an IReadOnlyList using a stream deserializer * Remove ParsedMappings from serialization/deserialization code on SourceMap --- .../SourceMap.cs | 32 +++++++++++++++++++ .../SourceMapParser.cs | 31 ++++++++++++------ 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/SourcemapToolkit.SourcemapParser/SourceMap.cs b/src/SourcemapToolkit.SourcemapParser/SourceMap.cs index 7fb95d9..9d16006 100644 --- a/src/SourcemapToolkit.SourcemapParser/SourceMap.cs +++ b/src/SourcemapToolkit.SourcemapParser/SourceMap.cs @@ -6,6 +6,36 @@ using Newtonsoft.Json; namespace SourcemapToolkit.SourcemapParser { + /// + /// A seemingly silly class, but unfortunately one which appears necessary. + /// While the SourceMap class is serializable, it is not deserializable directly. + /// The problem is that the IReadOnlyList generics throw the following exception: + /// 'Newtonsoft.Json.JsonSerializationException: Cannot create and populate list type' + /// when attempting to deserialize from a stream using a JsonTextReader. This + /// intermediary class mitigates the problem by reading the data into a mutable list + /// first. + /// + internal class SourceMapDeserializable + { + /// + public int Version; + + /// + public string File; + + /// + public string Mappings; + + /// + public List Sources; + + /// + public List Names; + + /// + public List SourcesContent; + } + public class SourceMap { /// @@ -42,6 +72,8 @@ namespace SourcemapToolkit.SourcemapParser /// /// Parsed version of the mappings string that is used for getting original names and source positions /// + /// Marked as JsonIgnore to because it isn't part of the actual format. See: https://sourcemaps.info/spec.html#h.mofvlxcwqzej + [JsonIgnore] public IReadOnlyList ParsedMappings { get; } /// diff --git a/src/SourcemapToolkit.SourcemapParser/SourceMapParser.cs b/src/SourcemapToolkit.SourcemapParser/SourceMapParser.cs index 2c12033..7a4cdc1 100644 --- a/src/SourcemapToolkit.SourcemapParser/SourceMapParser.cs +++ b/src/SourcemapToolkit.SourcemapParser/SourceMapParser.cs @@ -27,26 +27,37 @@ namespace SourcemapToolkit.SourcemapParser { JsonSerializer serializer = new JsonSerializer(); - SourceMap result = serializer.Deserialize(jsonTextReader); + SourceMapDeserializable deserializedSourceMap = serializer.Deserialize(jsonTextReader); // Since SourceMap is immutable we need to allocate a new one and copy over all the information - List parsedMappings = _mappingsListParser.ParseMappings(result.Mappings, result.Names, result.Sources); + List parsedMappings = _mappingsListParser.ParseMappings(deserializedSourceMap.Mappings, deserializedSourceMap.Names, deserializedSourceMap.Sources); // Resize to free unused memory - parsedMappings.Capacity = parsedMappings.Count; + RemoveExtraSpaceFromList(parsedMappings); + RemoveExtraSpaceFromList(deserializedSourceMap.Sources); + RemoveExtraSpaceFromList(deserializedSourceMap.Names); + RemoveExtraSpaceFromList(deserializedSourceMap.SourcesContent); - result = new SourceMap( - version: result.Version, - file: result.File, - mappings: result.Mappings, - sources: result.Sources, - names: result.Names, + SourceMap result = new SourceMap( + version: deserializedSourceMap.Version, + file: deserializedSourceMap.File, + mappings: deserializedSourceMap.Mappings, + sources: deserializedSourceMap.Sources, + names: deserializedSourceMap.Names, parsedMappings: parsedMappings, - sourcesContent: result.SourcesContent); + sourcesContent: deserializedSourceMap.SourcesContent); sourceMapStream.Close(); return result; } } + + private void RemoveExtraSpaceFromList(List list) + { + if (list != null) + { + list.Capacity = list.Count; + } + } } }