diff --git a/.editorconfig b/.editorconfig
index 05d66b5..d43e8f5 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -88,8 +88,10 @@ dotnet_diagnostic.CA1051.severity = none # CA1051: Do not declare visible instan
dotnet_diagnostic.CA1707.severity = none # CA1707: Identifiers should not contain underscores
dotnet_diagnostic.CA1716.severity = none # CA1716: Identifiers should not match keywords
dotnet_diagnostic.CA2201.severity = none # CA2201: Do not raise reserved exception types
+dotnet_diagnostic.CA2231.severity = none # CA2231: Overload operator equals on overriding ValueType.Equals
dotnet_diagnostic.CA2249.severity = none # CA2249: Consider using String.Contains instead of String.IndexOf
dotnet_diagnostic.IDE0045.severity = none # IDE0045: Use conditional expression for assignment
dotnet_diagnostic.IDE0046.severity = none # IDE0046: Convert to conditional expression
+dotnet_diagnostic.IDE0048.severity = none # IDE0048: Add parentheses for clarity
dotnet_diagnostic.IDE0058.severity = none # IDE0058: computed value is never used
diff --git a/README.md b/README.md
index 2a04d09..6daa7bb 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Source Map Tools [![Build Status](https://dev.azure.com/sourcemaptools/sourcemaptools/_apis/build/status/build?branchName=master)](https://dev.azure.com/sourcemaptools/sourcemaptools/_build/latest?definitionId=1&branchName=master) [![NuGet](https://img.shields.io/nuget/v/SourceMapTools.svg)](https://www.nuget.org/packages/SourceMapTools/)
+# Source Map Tools [![Build Status](https://img.shields.io/azure-devops/build/sourcemaptools/sourcemaptools/1/master?label=build%20(master))](https://dev.azure.com/sourcemaptools/sourcemaptools/_build/latest?definitionId=1&branchName=master) [![NuGet](https://img.shields.io/nuget/v/SourceMapTools.svg)](https://www.nuget.org/packages/SourceMapTools/) [![License](https://img.shields.io/github/license/MaceWindu/sourcemap-tools)](LICENSE.txt)
This is a C# library for working with JavaScript source maps and deminifying JavaScript callstacks.
diff --git a/ci/default.yml b/ci/default.yml
index fd9b2d1..63f9717 100644
--- a/ci/default.yml
+++ b/ci/default.yml
@@ -6,7 +6,7 @@ variables:
- name: assemblyVersion
value: 1.0.0
- name: packageVersion
- value: 1.0.0-rc.2
+ value: 1.0.0-rc.3
- name: nugetDevVersion
value: 1.0.0
diff --git a/src/SourceMapTools/CallstackDeminifier/BindingInformation.cs b/src/SourceMapTools/CallstackDeminifier/BindingInformation.cs
index 02e1fe0..e0deac7 100644
--- a/src/SourceMapTools/CallstackDeminifier/BindingInformation.cs
+++ b/src/SourceMapTools/CallstackDeminifier/BindingInformation.cs
@@ -6,7 +6,7 @@ namespace SourcemapToolkit.CallstackDeminifier
/// Describes information regarding a binding that can be used for minification.
/// Examples include methods, functions, and object declarations.
///
- internal class BindingInformation
+ internal struct BindingInformation
{
public BindingInformation(string name, SourcePosition sourcePosition)
{
@@ -16,11 +16,11 @@ public BindingInformation(string name, SourcePosition sourcePosition)
///
/// The name of the method or class
///
- public string Name { get; }
+ public readonly string Name;
///
/// The location of the function name or class declaration
///
- public SourcePosition SourcePosition { get; }
+ public readonly SourcePosition SourcePosition;
}
}
diff --git a/src/SourceMapTools/CallstackDeminifier/DeminifyStackTraceResult.cs b/src/SourceMapTools/CallstackDeminifier/DeminifyStackTraceResult.cs
index 0d02827..0846acc 100644
--- a/src/SourceMapTools/CallstackDeminifier/DeminifyStackTraceResult.cs
+++ b/src/SourceMapTools/CallstackDeminifier/DeminifyStackTraceResult.cs
@@ -1,29 +1,51 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
+using System.Text;
+using SourcemapToolkit.SourcemapParser;
namespace SourcemapToolkit.CallstackDeminifier
{
+ ///
+ /// Contains stack trace details (both minified and diminified).
+ ///
public class DeminifyStackTraceResult
{
internal DeminifyStackTraceResult(
+ string? message,
IReadOnlyList minifiedStackFrames,
- IReadOnlyList deminifiedStackFrameResults,
- string? message)
+ IReadOnlyList deminifiedStackFrameResults)
{
MinifiedStackFrames = minifiedStackFrames;
DeminifiedStackFrameResults = deminifiedStackFrameResults;
Message = message;
}
+ ///
+ /// Gets error message, associated with stack trace.
+ ///
public string? Message { get; }
+ ///
+ /// Gets list of stack frames for minified stack.
+ ///
public IReadOnlyList MinifiedStackFrames { get; }
+ ///
+ /// Gets list of stack frames for de-minified stack.
+ ///
public IReadOnlyList DeminifiedStackFrameResults { get; }
+ ///
+ /// Returns string that represents stack trace
+ ///
public override string ToString()
{
- var output = Message ?? string.Empty;
+ var sb = new StringBuilder();
+
+ if (!string.IsNullOrEmpty(Message))
+ {
+ sb.Append(Message);
+ }
+
for (var i = 0; i < DeminifiedStackFrameResults.Count; i++)
{
var deminFrame = DeminifiedStackFrameResults[i].DeminifiedStackFrame;
@@ -31,13 +53,16 @@ public override string ToString()
// Use deminified info wherever possible, merging if necessary so we always print a full frame
var frame = new StackFrame(
deminFrame.MethodName ?? MinifiedStackFrames[i].MethodName,
- deminFrame.SourcePosition != null ? deminFrame.FilePath : MinifiedStackFrames[i].FilePath,
- deminFrame.SourcePosition ?? MinifiedStackFrames[i].SourcePosition);
+ deminFrame.SourcePosition != SourcePosition.NotFound ? deminFrame.FilePath : MinifiedStackFrames[i].FilePath,
+ deminFrame.SourcePosition != SourcePosition.NotFound ? deminFrame.SourcePosition : MinifiedStackFrames[i].SourcePosition);
- output += $"{Environment.NewLine} {frame}";
+ sb
+ .AppendLine()
+ .Append(" ")
+ .Append(frame);
}
- return output;
+ return sb.ToString();
}
}
}
diff --git a/src/SourceMapTools/CallstackDeminifier/FunctionFinderVisitor.cs b/src/SourceMapTools/CallstackDeminifier/FunctionFinderVisitor.cs
index a50e766..be13ca4 100644
--- a/src/SourceMapTools/CallstackDeminifier/FunctionFinderVisitor.cs
+++ b/src/SourceMapTools/CallstackDeminifier/FunctionFinderVisitor.cs
@@ -13,8 +13,15 @@ namespace SourcemapToolkit.CallstackDeminifier
///
internal class FunctionFinderVisitor : AllAstVisitor
{
+ private readonly SourceMap _sourceMap;
+
internal List FunctionMap { get; } = new List();
+ public FunctionFinderVisitor(SourceMap sourceMap)
+ {
+ _sourceMap = sourceMap;
+ }
+
protected override void VisitArrowFunctionExpression(ArrowFunctionExpression arrowFunctionExpression)
{
base.VisitArrowFunctionExpression(arrowFunctionExpression);
@@ -42,6 +49,7 @@ private void VisitFunction(IFunction function)
{
var functionMapEntry = new FunctionMapEntry(
bindings,
+ _sourceMap.GetDeminifiedMethodName(bindings),
GetSourcePosition(function.Body.Location.Start),
GetSourcePosition(function.Body.Location.End));
diff --git a/src/SourceMapTools/CallstackDeminifier/FunctionMapConsumer.cs b/src/SourceMapTools/CallstackDeminifier/FunctionMapConsumer.cs
index 06d00bb..e0fcb7a 100644
--- a/src/SourceMapTools/CallstackDeminifier/FunctionMapConsumer.cs
+++ b/src/SourceMapTools/CallstackDeminifier/FunctionMapConsumer.cs
@@ -16,7 +16,7 @@ internal class FunctionMapConsumer : IFunctionMapConsumer
{
foreach (var mapEntry in functionMap)
{
- if (mapEntry.StartSourcePosition < sourcePosition && mapEntry.EndSourcePosition > sourcePosition)
+ if (mapEntry.Start < sourcePosition && mapEntry.End > sourcePosition)
{
return mapEntry;
}
diff --git a/src/SourceMapTools/CallstackDeminifier/FunctionMapEntry.cs b/src/SourceMapTools/CallstackDeminifier/FunctionMapEntry.cs
index 2b1eb80..c4ebc1b 100644
--- a/src/SourceMapTools/CallstackDeminifier/FunctionMapEntry.cs
+++ b/src/SourceMapTools/CallstackDeminifier/FunctionMapEntry.cs
@@ -10,12 +10,14 @@ internal class FunctionMapEntry
{
public FunctionMapEntry(
IReadOnlyList bindings,
- SourcePosition startSourcePosition,
- SourcePosition endSourcePosition)
+ string? deminifiedMethodName,
+ SourcePosition start,
+ SourcePosition end)
{
Bindings = bindings;
- StartSourcePosition = startSourcePosition;
- EndSourcePosition = endSourcePosition;
+ DeminifiedMethodName = deminifiedMethodName;
+ Start = start;
+ End = end;
}
///
@@ -29,16 +31,16 @@ public FunctionMapEntry(
/// If this entry represents a function whose name was minified, this value
/// may contain an associated deminfied name corresponding to the function.
///
- public string? DeminfifiedMethodName { get; set; }
+ public string? DeminifiedMethodName { get; }
///
/// Denotes the location of the beginning of this function
///
- public SourcePosition StartSourcePosition { get; }
+ public SourcePosition Start { get; }
///
/// Denotes the end location of this function
///
- public SourcePosition EndSourcePosition { get; }
+ public SourcePosition End { get; }
}
}
diff --git a/src/SourceMapTools/CallstackDeminifier/FunctionMapGenerator.cs b/src/SourceMapTools/CallstackDeminifier/FunctionMapGenerator.cs
index dbc8ad0..8355596 100644
--- a/src/SourceMapTools/CallstackDeminifier/FunctionMapGenerator.cs
+++ b/src/SourceMapTools/CallstackDeminifier/FunctionMapGenerator.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.IO;
using Esprima;
using SourcemapToolkit.SourcemapParser;
@@ -22,12 +21,7 @@ internal class FunctionMapGenerator : IFunctionMapGenerator
IReadOnlyList? result;
try
{
- result = ParseSourceCode(sourceCodeStream);
-
- foreach (var functionMapEntry in result)
- {
- functionMapEntry.DeminfifiedMethodName = GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap);
- }
+ result = ParseSourceCode(sourceCodeStream, sourceMap);
}
catch
{
@@ -42,7 +36,7 @@ internal class FunctionMapGenerator : IFunctionMapGenerator
///
/// Iterates over all the code in the JavaScript file to get a list of all the functions declared in that file.
///
- internal static IReadOnlyList ParseSourceCode(Stream sourceCodeStream)
+ internal static IReadOnlyList ParseSourceCode(Stream sourceCodeStream, SourceMap sourceMap)
{
string sourceCode;
using (sourceCodeStream)
@@ -55,61 +49,16 @@ internal static IReadOnlyList ParseSourceCode(Stream sourceCod
var script = jsParser.ParseScript();
- var functionFinderVisitor = new FunctionFinderVisitor();
+ var functionFinderVisitor = new FunctionFinderVisitor(sourceMap);
functionFinderVisitor.Visit(script);
- // Sort in descending order by start position
- functionFinderVisitor.FunctionMap.Sort((x, y) => y.StartSourcePosition.CompareTo(x.StartSourcePosition));
+ // Sort in descending order by start position. This allows the first result found in a linear search to be the "closest function to the [consumer's] source position".
+ //
+ // ATTN: It may be possible to do this with an ascending order sort, followed by a series of binary searches on rows & columns.
+ // Our current profiles show the memory pressure being a bigger issue than the stack lookup, so I'm leaving this for now.
+ functionFinderVisitor.FunctionMap.Sort((x, y) => y.Start.CompareTo(x.Start));
return functionFinderVisitor.FunctionMap;
}
-
- ///
- /// Gets the original name corresponding to a function based on the information provided in the source map.
- ///
- internal static string? GetDeminifiedMethodNameFromSourceMap(FunctionMapEntry wrappingFunction, SourceMap sourceMap)
- {
- if (wrappingFunction == null)
- {
- throw new ArgumentNullException(nameof(wrappingFunction));
- }
-
- if (sourceMap == null)
- {
- throw new ArgumentNullException(nameof(sourceMap));
- }
-
- if (wrappingFunction.Bindings != null && wrappingFunction.Bindings.Count > 0)
- {
- var entryNames = new List();
-
- foreach (var binding in wrappingFunction.Bindings)
- {
- var entry = sourceMap.GetMappingEntryForGeneratedSourcePosition(binding.SourcePosition);
- if (entry != null && entry.OriginalName != null)
- {
- entryNames.Add(entry.OriginalName);
- }
- }
-
- // // The object name already contains the method name, so do not append it
- if (entryNames.Count > 1 && entryNames[entryNames.Count - 2].EndsWith("." + entryNames[entryNames.Count - 1], StringComparison.Ordinal))
- {
- entryNames.RemoveAt(entryNames.Count - 1);
- }
-
- if (entryNames.Count > 2 && entryNames[entryNames.Count - 2] == "prototype")
- {
- entryNames.RemoveAt(entryNames.Count - 2);
- }
-
- if (entryNames.Count > 0)
- {
- return string.Join(".", entryNames);
- }
- }
-
- return null;
- }
}
}
diff --git a/src/SourceMapTools/CallstackDeminifier/IReadOnlyListExtensions.cs b/src/SourceMapTools/CallstackDeminifier/IReadOnlyListExtensions.cs
new file mode 100644
index 0000000..9253df4
--- /dev/null
+++ b/src/SourceMapTools/CallstackDeminifier/IReadOnlyListExtensions.cs
@@ -0,0 +1,52 @@
+using System.Collections.Generic;
+
+namespace SourcemapToolkit.SourcemapParser
+{
+ internal static class IReadOnlyListExtensions
+ {
+ public static int IndexOf(this IReadOnlyList input, T value)
+ {
+ var equalityComparer = EqualityComparer.Default;
+ for (var i = 0; i < input.Count; i++)
+ {
+ if (equalityComparer.Equals(input[i], value))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ ///
+ /// Copied from: https://referencesource.microsoft.com/#mscorlib/system/collections/generic/arraysorthelper.cs,63a9955a91f2b37b
+ ///
+ public static int BinarySearch(this IReadOnlyList input, T item, IComparer comparer)
+ {
+ var lo = 0;
+ var hi = input.Count - 1;
+
+ while (lo <= hi)
+ {
+ var i = lo + ((hi - lo) >> 1);
+ var order = comparer.Compare(input[i], item);
+
+ if (order == 0)
+ {
+ return i;
+ }
+
+ if (order < 0)
+ {
+ lo = i + 1;
+ }
+ else
+ {
+ hi = i - 1;
+ }
+ }
+
+ return ~lo;
+ }
+ }
+}
diff --git a/src/SourceMapTools/CallstackDeminifier/IStackTraceParser.cs b/src/SourceMapTools/CallstackDeminifier/IStackTraceParser.cs
index 732ada0..be69456 100644
--- a/src/SourceMapTools/CallstackDeminifier/IStackTraceParser.cs
+++ b/src/SourceMapTools/CallstackDeminifier/IStackTraceParser.cs
@@ -2,6 +2,9 @@
namespace SourcemapToolkit.CallstackDeminifier
{
+ ///
+ /// Stack trace parser contract.
+ ///
public interface IStackTraceParser
{
///
diff --git a/src/SourceMapTools/CallstackDeminifier/MethodNameStackFrameDeminifier.cs b/src/SourceMapTools/CallstackDeminifier/MethodNameStackFrameDeminifier.cs
index 5c4c44e..9f7ffcc 100644
--- a/src/SourceMapTools/CallstackDeminifier/MethodNameStackFrameDeminifier.cs
+++ b/src/SourceMapTools/CallstackDeminifier/MethodNameStackFrameDeminifier.cs
@@ -50,7 +50,7 @@ StackFrameDeminificationResult IStackFrameDeminifier.DeminifyStackFrame(StackFra
return new StackFrameDeminificationResult(
deminificationError,
- new StackFrame(wrappingFunction?.DeminfifiedMethodName));
+ new StackFrame(wrappingFunction?.DeminifiedMethodName));
}
}
}
\ No newline at end of file
diff --git a/src/SourceMapTools/CallstackDeminifier/SourceMapExtensions.cs b/src/SourceMapTools/CallstackDeminifier/SourceMapExtensions.cs
new file mode 100644
index 0000000..e6d71d0
--- /dev/null
+++ b/src/SourceMapTools/CallstackDeminifier/SourceMapExtensions.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using SourcemapToolkit.SourcemapParser;
+
+namespace SourcemapToolkit.CallstackDeminifier
+{
+ internal static class SourceMapExtensions
+ {
+ ///
+ /// Gets the original name corresponding to a function based on the information provided in the source map.
+ ///
+ internal static string? GetDeminifiedMethodName(this SourceMap sourceMap, IReadOnlyList bindings)
+ {
+ if (bindings.Count > 0)
+ {
+ var entryNames = new List();
+
+ foreach (var binding in bindings)
+ {
+ var entry = sourceMap.GetMappingEntryForGeneratedSourcePosition(binding.SourcePosition);
+ if (entry != null && entry.Value.OriginalName != null)
+ {
+ entryNames.Add(entry.Value.OriginalName);
+ }
+ }
+
+ // // The object name already contains the method name, so do not append it
+ if (entryNames.Count > 1
+ && entryNames[entryNames.Count - 2].Length > entryNames[entryNames.Count - 1].Length
+ && entryNames[entryNames.Count - 2].EndsWith(entryNames[entryNames.Count - 1], StringComparison.Ordinal)
+ && entryNames[entryNames.Count - 2][entryNames[entryNames.Count - 2].Length - 1 - entryNames[entryNames.Count - 1].Length] == '.')
+ {
+ entryNames.RemoveAt(entryNames.Count - 1);
+ }
+
+ if (entryNames.Count > 2 && entryNames[entryNames.Count - 2] == "prototype")
+ {
+ entryNames.RemoveAt(entryNames.Count - 2);
+ }
+
+ if (entryNames.Count > 0)
+ {
+ return string.Join(".", entryNames);
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/SourceMapTools/CallstackDeminifier/StackFrame.cs b/src/SourceMapTools/CallstackDeminifier/StackFrame.cs
index b491558..1b65688 100644
--- a/src/SourceMapTools/CallstackDeminifier/StackFrame.cs
+++ b/src/SourceMapTools/CallstackDeminifier/StackFrame.cs
@@ -7,12 +7,22 @@ namespace SourcemapToolkit.CallstackDeminifier
///
public class StackFrame
{
+ ///
+ /// Creates new instance stack frame with specified method name.
+ ///
+ /// Name of method, corresponding to current stack frame.
public StackFrame(string? methodName)
- : this(methodName, null, null)
+ : this(methodName, null, SourcePosition.NotFound)
{
}
- public StackFrame(string? methodName, string? filePath, SourcePosition? sourcePosition)
+ ///
+ /// Creates new instance stack frame with specified method name and source position.
+ ///
+ /// Name of method, corresponding to current stack frame.
+ /// Path to file that contains current stack frame method.
+ /// Position of stack frame in source file.
+ public StackFrame(string? methodName, string? filePath, SourcePosition sourcePosition)
{
MethodName = methodName;
FilePath = filePath;
@@ -32,15 +42,18 @@ public StackFrame(string? methodName, string? filePath, SourcePosition? sourcePo
///
/// The zero-based position of this stack entry.
///
- public SourcePosition? SourcePosition { get; internal set; }
+ public SourcePosition SourcePosition { get; internal set; } = SourcePosition.NotFound;
+ ///
+ /// Returns text representation of current stack frame.
+ ///
public override string ToString()
{
var output = $"at {(string.IsNullOrWhiteSpace(MethodName) ? "?" : MethodName)}";
if (!string.IsNullOrWhiteSpace(FilePath))
{
output += $" in {FilePath}";
- if (SourcePosition != null)
+ if (SourcePosition != SourcePosition.NotFound)
{
output += $":{SourcePosition.Line + 1}:{SourcePosition.Column + 1}";
}
diff --git a/src/SourceMapTools/CallstackDeminifier/StackFrameDeminifier.cs b/src/SourceMapTools/CallstackDeminifier/StackFrameDeminifier.cs
index 99ddfb9..9c49851 100644
--- a/src/SourceMapTools/CallstackDeminifier/StackFrameDeminifier.cs
+++ b/src/SourceMapTools/CallstackDeminifier/StackFrameDeminifier.cs
@@ -68,9 +68,9 @@ StackFrameDeminificationResult IStackFrameDeminifier.DeminifyStackFrame(StackFra
}
else
{
- result.DeminifiedStackFrame.FilePath = generatedSourcePositionMappingEntry.OriginalFileName;
- result.DeminifiedStackFrame.SourcePosition = generatedSourcePositionMappingEntry.OriginalSourcePosition;
- result.DeminifiedSymbolName = generatedSourcePositionMappingEntry.OriginalName;
+ result.DeminifiedStackFrame.FilePath = generatedSourcePositionMappingEntry.Value.OriginalFileName;
+ result.DeminifiedStackFrame.SourcePosition = generatedSourcePositionMappingEntry.Value.OriginalSourcePosition;
+ result.DeminifiedSymbolName = generatedSourcePositionMappingEntry.Value.OriginalName;
}
}
diff --git a/src/SourceMapTools/CallstackDeminifier/StackTraceDeminifier.cs b/src/SourceMapTools/CallstackDeminifier/StackTraceDeminifier.cs
index c8b06e3..c5c2c06 100644
--- a/src/SourceMapTools/CallstackDeminifier/StackTraceDeminifier.cs
+++ b/src/SourceMapTools/CallstackDeminifier/StackTraceDeminifier.cs
@@ -24,7 +24,7 @@ internal StackTraceDeminifier(IStackFrameDeminifier stackFrameDeminifier, IStack
public DeminifyStackTraceResult DeminifyStackTrace(string stackTraceString)
{
var minifiedFrames = _stackTraceParser.ParseStackTrace(stackTraceString, out var message);
- var deminifiedFrames = new List();
+ var deminifiedFrames = new List(minifiedFrames.Count);
// Deminify frames in reverse order so we can pass the symbol name from caller
// (i.e. the function name) into the next level's deminification.
@@ -33,10 +33,12 @@ public DeminifyStackTraceResult DeminifyStackTrace(string stackTraceString)
{
var frame = _stackFrameDeminifier.DeminifyStackFrame(minifiedFrames[i], callerSymbolName);
callerSymbolName = frame.DeminifiedSymbolName;
- deminifiedFrames.Insert(0, frame);
+ deminifiedFrames.Add(frame);
}
- return new DeminifyStackTraceResult(minifiedFrames, deminifiedFrames, message);
+ deminifiedFrames.Reverse();
+
+ return new DeminifyStackTraceResult(message, minifiedFrames, deminifiedFrames);
}
}
}
diff --git a/src/SourceMapTools/CallstackDeminifier/StackTraceDeminfierFactory.cs b/src/SourceMapTools/CallstackDeminifier/StackTraceDeminifierFactory.cs
similarity index 98%
rename from src/SourceMapTools/CallstackDeminifier/StackTraceDeminfierFactory.cs
rename to src/SourceMapTools/CallstackDeminifier/StackTraceDeminifierFactory.cs
index b168eca..91d0ea3 100644
--- a/src/SourceMapTools/CallstackDeminifier/StackTraceDeminfierFactory.cs
+++ b/src/SourceMapTools/CallstackDeminifier/StackTraceDeminifierFactory.cs
@@ -3,7 +3,10 @@
namespace SourcemapToolkit.CallstackDeminifier
{
- public class StackTraceDeminfierFactory
+ ///
+ /// Provides factory methods to create stack trace deminifier instance.
+ ///
+ public class StackTraceDeminifierFactory
{
private static void ValidateArguments(ISourceMapProvider sourceMapProvider, ISourceCodeProvider generatedCodeProvider, IStackTraceParser stackTraceParser)
{
diff --git a/src/SourceMapTools/CallstackDeminifier/StackTraceParser.cs b/src/SourceMapTools/CallstackDeminifier/StackTraceParser.cs
index f446f6c..c926d20 100644
--- a/src/SourceMapTools/CallstackDeminifier/StackTraceParser.cs
+++ b/src/SourceMapTools/CallstackDeminifier/StackTraceParser.cs
@@ -31,7 +31,7 @@ public class StackTraceParser : IStackTraceParser
///
IReadOnlyList IStackTraceParser.ParseStackTrace(string stackTraceString)
{
- return ParseStackTrace(stackTraceString, out var _);
+ return ParseStackTrace(stackTraceString, out _);
}
///
@@ -53,19 +53,23 @@ public virtual IReadOnlyList ParseStackTrace(string stackTraceString
}
message = null;
- var stackTrace = new List();
- var stackFrameStrings = stackTraceString.Split('\n').ToList();
+
+ var stackFrameStrings = stackTraceString.SplitFast('\n');
+
+ var startingIndex = 0;
var firstFrame = stackFrameStrings.First();
if (!firstFrame.StartsWith(" ", StringComparison.Ordinal) && TryExtractMethodNameFromFrame(firstFrame) == null)
{
message = firstFrame.Trim();
- stackFrameStrings.RemoveAt(0);
+ startingIndex = 1;
}
- foreach (var frame in stackFrameStrings)
+ var stackTrace = new List(stackFrameStrings.Length - startingIndex);
+
+ for (var i = startingIndex; i < stackFrameStrings.Length; i++)
{
- var parsedStackFrame = TryParseSingleStackFrame(frame);
+ var parsedStackFrame = TryParseSingleStackFrame(stackFrameStrings[i]);
if (parsedStackFrame != null)
{
diff --git a/src/SourceMapTools/SourcemapParser/MappingEntry.cs b/src/SourceMapTools/SourcemapParser/MappingEntry.cs
index 31186bd..2ea107c 100644
--- a/src/SourceMapTools/SourcemapParser/MappingEntry.cs
+++ b/src/SourceMapTools/SourcemapParser/MappingEntry.cs
@@ -1,12 +1,26 @@
namespace SourcemapToolkit.SourcemapParser
{
- public class MappingEntry
+ ///
+ /// Source map entry.
+ ///
+ public struct MappingEntry
{
+ ///
+ /// Creates source map entry instance.
+ ///
+ /// Entry position in minified script.
public MappingEntry(SourcePosition generatedSourcePosition)
: this(generatedSourcePosition, null, null, null)
{
}
+ ///
+ /// Creates source map entry instance.
+ ///
+ /// Entry position in minified script.
+ /// Entry position in original script.
+ /// Original name of source map entry.
+ /// File name of original source file with entry code.
public MappingEntry(
SourcePosition generatedSourcePosition,
SourcePosition? originalSourcePosition,
@@ -14,7 +28,7 @@ public MappingEntry(
string? originalFileName)
{
GeneratedSourcePosition = generatedSourcePosition;
- OriginalSourcePosition = originalSourcePosition;
+ OriginalSourcePosition = originalSourcePosition ?? SourcePosition.NotFound;
OriginalName = originalName;
OriginalFileName = originalFileName;
}
@@ -22,40 +36,85 @@ public MappingEntry(
///
/// The location of the line of code in the transformed code
///
- public SourcePosition GeneratedSourcePosition { get; internal set; }
+ public readonly SourcePosition GeneratedSourcePosition { get; }
///
/// The location of the code in the original source code
///
- public SourcePosition? OriginalSourcePosition { get; internal set; }
+ public readonly SourcePosition OriginalSourcePosition { get; }
///
/// The original name of the code referenced by this mapping entry
///
- public string? OriginalName { get; internal set; }
+ public readonly string? OriginalName { get; }
///
/// The name of the file that originally contained this code
///
- public string? OriginalFileName { get; internal set; }
+ public readonly string? OriginalFileName { get; }
- public MappingEntry Clone()
+ ///
+ /// Returns copy of entry with source positions having zero as column number.
+ ///
+ /// Returns copy of current entry.
+ public MappingEntry CloneWithResetColumnNumber()
{
return new MappingEntry(
- GeneratedSourcePosition.Clone(),
- OriginalSourcePosition?.Clone(),
+ new SourcePosition(GeneratedSourcePosition.Line, 0),
+ new SourcePosition(OriginalSourcePosition.Line, 0),
OriginalName,
OriginalFileName);
}
+ ///
+ /// Compares current mapping entry whith another one.
+ ///
+ /// Mapping entry to compare with.
+ ///
+ ///
+ /// - true: both entries are the same.
+ /// - false: entries differ from each other.
+ ///
+ ///
public bool IsValueEqual(MappingEntry anEntry)
{
return
OriginalName == anEntry.OriginalName &&
OriginalFileName == anEntry.OriginalFileName &&
- GeneratedSourcePosition.CompareTo(anEntry.GeneratedSourcePosition) == 0 &&
- ((OriginalSourcePosition == null && anEntry.OriginalSourcePosition == null)
- || (OriginalSourcePosition != null && anEntry.OriginalSourcePosition != null && OriginalSourcePosition.CompareTo(anEntry.OriginalSourcePosition) == 0));
+ GeneratedSourcePosition.Equals(anEntry.GeneratedSourcePosition) &&
+ OriginalSourcePosition.Equals(anEntry.OriginalSourcePosition);
+ }
+
+ ///
+ /// Compares current mapping entry whith another one.
+ ///
+ /// Mapping entry to compare with.
+ ///
+ ///
+ /// - true: both entries are the same.
+ /// - false: entries differ from each other oth is not an instance of .
+ ///
+ ///
+ public override bool Equals(object? obj)
+ {
+ return obj is MappingEntry mappingEntry && IsValueEqual(mappingEntry);
+ }
+
+ ///
+ /// An implementation of Josh Bloch's hashing algorithm from Effective Java.
+ /// It is fast, offers a good distribution (with primes 23 and 31), and allocation free.
+ ///
+ public override int GetHashCode()
+ {
+ unchecked // Wrap to protect overflow
+ {
+ var hash = 23;
+ hash = hash * 31 + GeneratedSourcePosition.GetHashCode();
+ hash = hash * 31 + OriginalSourcePosition.GetHashCode();
+ hash = hash * 31 + (OriginalName ?? "").GetHashCode();
+ hash = hash * 31 + (OriginalFileName ?? "").GetHashCode();
+ return hash;
+ }
}
}
}
\ No newline at end of file
diff --git a/src/SourceMapTools/SourcemapParser/MappingGenerateState.cs b/src/SourceMapTools/SourcemapParser/MappingGenerateState.cs
index 19a3ca6..7edd71a 100644
--- a/src/SourceMapTools/SourcemapParser/MappingGenerateState.cs
+++ b/src/SourceMapTools/SourcemapParser/MappingGenerateState.cs
@@ -10,22 +10,22 @@ internal class MappingGenerateState
///
/// Last location of the code in the transformed code
///
- public SourcePosition LastGeneratedPosition { get; private set; } = new SourcePosition();
+ public SourcePosition LastGeneratedPosition { get; private set; }
///
/// Last location of the code in the source code
///
- public SourcePosition LastOriginalPosition { get; } = new SourcePosition();
+ public SourcePosition LastOriginalPosition { get; internal set; }
///
/// List that contains the symbol names
///
- public IList Names { get; }
+ public IReadOnlyList Names { get; }
///
/// List that contains the file sources
///
- public IList Sources { get; }
+ public IReadOnlyList Sources { get; }
///
/// Index of last file source
@@ -42,11 +42,21 @@ internal class MappingGenerateState
///
public bool IsFirstSegment { get; set; }
- public MappingGenerateState(IList names, IList sources)
+ public MappingGenerateState(IReadOnlyList names, IReadOnlyList sources)
{
Names = names;
Sources = sources;
IsFirstSegment = true;
}
+
+ public void AdvanceLastGeneratedPositionLine()
+ {
+ LastGeneratedPosition = new SourcePosition(LastGeneratedPosition.Line + 1, 0);
+ }
+
+ public void UpdateLastGeneratedPositionColumn(int zeroBasedColumnNumber)
+ {
+ LastGeneratedPosition = new SourcePosition(LastGeneratedPosition.Line, zeroBasedColumnNumber);
+ }
}
}
diff --git a/src/SourceMapTools/SourcemapParser/MappingListParser.cs b/src/SourceMapTools/SourcemapParser/MappingListParser.cs
index 79e3f6d..4a21b87 100644
--- a/src/SourceMapTools/SourcemapParser/MappingListParser.cs
+++ b/src/SourceMapTools/SourcemapParser/MappingListParser.cs
@@ -9,6 +9,8 @@ namespace SourcemapToolkit.SourcemapParser
///
internal class MappingsListParser
{
+ private static readonly char[] LineDelimiter = new[] { ',' };
+
///
/// Parses a single "segment" of the mapping field for a source map. A segment describes one piece of code in the generated source.
/// In the mapping string "AAaAA,CAACC;", AAaAA and CAACC are both segments. This method assumes the segments have already been decoded
@@ -24,7 +26,7 @@ internal static NumericMappingEntry ParseSingleMappingSegment(IReadOnlyList
throw new ArgumentOutOfRangeException(nameof(segmentFields));
}
- var numericMappingEntry = new NumericMappingEntry
+ var numericMappingEntry = new NumericMappingEntry()
{
GeneratedLineNumber = mappingsParserState.CurrentGeneratedLineNumber,
GeneratedColumnNumber = mappingsParserState.CurrentGeneratedColumnBase + segmentFields[0]
@@ -79,16 +81,21 @@ internal static List ParseMappings(string mappingString, IReadOnly
// The V3 source map format calls for all Base64 VLQ segments to be seperated by commas.
// Each line of generated code is separated using semicolons. The count of semicolons encountered gives the current line number.
- var lines = mappingString.Split(';');
+ var lines = mappingString.SplitFast(';');
- for (var lineNumber = 0; lineNumber < lines.Length; lineNumber += 1)
+ for (var lineNumber = 0; lineNumber < lines.Length; lineNumber++)
{
// The only value that resets when encountering a semicolon is the starting column.
- currentMappingsParserState = new MappingsParserState(currentMappingsParserState, newGeneratedLineNumber: lineNumber, newGeneratedColumnBase: 0);
- var segmentsForLine = lines[lineNumber].Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ currentMappingsParserState = new MappingsParserState(
+ currentMappingsParserState,
+ newGeneratedLineNumber: lineNumber,
+ newGeneratedColumnBase: 0);
+
+ var segmentsForLine = lines[lineNumber].Split(LineDelimiter, StringSplitOptions.RemoveEmptyEntries);
foreach (var segment in segmentsForLine)
{
+ // Reuse the numericMappingEntry to ease GC allocations.
var numericMappingEntry = ParseSingleMappingSegment(Base64VlqDecoder.Decode(segment), currentMappingsParserState);
mappingEntries.Add(numericMappingEntry.ToMappingEntry(names, sources));
diff --git a/src/SourceMapTools/SourcemapParser/NumericMappingEntry.cs b/src/SourceMapTools/SourcemapParser/NumericMappingEntry.cs
index b3ce10e..9bbc263 100644
--- a/src/SourceMapTools/SourcemapParser/NumericMappingEntry.cs
+++ b/src/SourceMapTools/SourcemapParser/NumericMappingEntry.cs
@@ -7,7 +7,7 @@ namespace SourcemapToolkit.SourcemapParser
/// Corresponds to a single parsed entry in the source map mapping string that is used internally by the parser.
/// The public API exposes the MappingEntry object, which is more useful to consumers of the library.
///
- internal class NumericMappingEntry
+ internal struct NumericMappingEntry
{
///
/// The zero-based line number in the generated code that corresponds to this mapping segment.
@@ -41,18 +41,23 @@ internal class NumericMappingEntry
public MappingEntry ToMappingEntry(IReadOnlyList names, IReadOnlyList sources)
{
- var result = new MappingEntry(new SourcePosition(GeneratedLineNumber, GeneratedColumnNumber));
+ SourcePosition originalSourcePosition;
if (OriginalColumnNumber.HasValue && OriginalLineNumber.HasValue)
{
- result.OriginalSourcePosition = new SourcePosition(OriginalLineNumber.Value, OriginalColumnNumber.Value);
+ originalSourcePosition = new SourcePosition(OriginalLineNumber.Value, OriginalColumnNumber.Value);
+ }
+ else
+ {
+ originalSourcePosition = SourcePosition.NotFound;
}
+ string? originalName = null;
if (OriginalNameIndex.HasValue)
{
try
{
- result.OriginalName = names[OriginalNameIndex.Value];
+ originalName = names[OriginalNameIndex.Value];
}
catch (IndexOutOfRangeException e)
{
@@ -61,11 +66,12 @@ public MappingEntry ToMappingEntry(IReadOnlyList names, IReadOnlyList names, IReadOnlyList
+ /// Source map object.
+ ///
public class SourceMap
{
+ ///
+ /// Cache the comparer to save the allocation.
+ ///
+ [JsonIgnore]
+ private static readonly Comparer _comparer = Comparer.Create((a, b) => a.GeneratedSourcePosition.CompareTo(b.GeneratedSourcePosition));
+
+
///
/// The version of the source map specification being used
///
@@ -29,40 +39,68 @@ public class SourceMap
/// The list of source files that were the inputs used to generate this output file
///
[JsonPropertyName("sources")]
- public List? Sources { get; set; }
+ public IReadOnlyList? Sources { get; set; }
///
/// A list of known original names for entries in this file
///
[JsonPropertyName("names")]
- public List? Names { get; set; }
+ public IReadOnlyList? Names { get; set; }
///
/// Parsed version of the mappings string that is used for getting original names and source positions
///
[JsonIgnore]
- public List ParsedMappings { get; internal set; } = new List();
+ public IReadOnlyList ParsedMappings { get; internal set; } = null!;
///
/// A list of content source files
///
- public List? SourcesContent { get; set; }
+ public IReadOnlyList? SourcesContent { get; set; }
- public SourceMap Clone()
+ ///
+ /// Json deserialization constructor.
+ ///
+ public SourceMap()
{
- var sourceMap = new SourceMap()
- {
- Version = Version,
- File = File,
- Mappings = Mappings,
- Sources = new List(Sources),
- Names = new List(Names),
- SourcesContent = new List(SourcesContent)
- };
+ }
- sourceMap.ParsedMappings.AddRange(ParsedMappings.Select(m => m.Clone()));
+ ///
+ /// Creates new instance of source map object.
+ /// See for more details.
+ ///
+ public SourceMap(
+ int version,
+ string? file,
+ string? mappings,
+ IReadOnlyList? sources,
+ IReadOnlyList? names,
+ IReadOnlyList parsedMappings,
+ IReadOnlyList? sourcesContent)
+ {
+ Version = version;
+ File = file;
+ Mappings = mappings;
+ Sources = sources;
+ Names = names;
+ ParsedMappings = parsedMappings;
+ SourcesContent = sourcesContent;
+ }
- return sourceMap;
+ ///
+ /// Creates copy fo source map.
+ ///
+ /// Returns copy of current source map object.
+ public SourceMap Clone()
+ {
+ return new SourceMap(
+ Version,
+ File,
+ Mappings,
+ Sources,
+ Names,
+ ParsedMappings,
+ SourcesContent);
}
///
@@ -85,27 +123,22 @@ public SourceMap ApplySourceMap(SourceMap submap, string? sourceFile = null)
{
if (submap.File == null)
{
- throw new Exception("ApplySourceMap expects either the explicit source file to the map, or submap's 'file' property");
+ throw new Exception($"{nameof(ApplySourceMap)} expects either the explicit source file to the map, or submap's 'file' property");
}
sourceFile = submap.File;
}
- var newSourceMap = new SourceMap()
- {
- File = File,
- Version = Version,
- Sources = new List(),
- Names = new List(),
- SourcesContent = new List()
- };
+ var sources = new HashSet(StringComparer.Ordinal);
+ var names = new HashSet(StringComparer.Ordinal);
+ var parsedMappings = new List(ParsedMappings.Count);
// transform mappings in this source map
foreach (var mappingEntry in ParsedMappings)
{
- var newMappingEntry = mappingEntry.Clone();
+ var newMappingEntry = mappingEntry;
- if (mappingEntry.OriginalFileName == sourceFile && mappingEntry.OriginalSourcePosition != null)
+ if (mappingEntry.OriginalFileName == sourceFile && mappingEntry.OriginalSourcePosition != SourcePosition.NotFound)
{
var correspondingSubMapMappingEntry = submap.GetMappingEntryForGeneratedSourcePosition(mappingEntry.OriginalSourcePosition);
@@ -113,10 +146,10 @@ public SourceMap ApplySourceMap(SourceMap submap, string? sourceFile = null)
{
// Copy the mapping
newMappingEntry = new MappingEntry(
- mappingEntry.GeneratedSourcePosition.Clone(),
- correspondingSubMapMappingEntry.OriginalSourcePosition?.Clone(),
- correspondingSubMapMappingEntry.OriginalName ?? mappingEntry.OriginalName,
- correspondingSubMapMappingEntry.OriginalFileName ?? mappingEntry.OriginalFileName);
+ mappingEntry.GeneratedSourcePosition,
+ correspondingSubMapMappingEntry.Value.OriginalSourcePosition,
+ correspondingSubMapMappingEntry.Value.OriginalName ?? mappingEntry.OriginalName,
+ correspondingSubMapMappingEntry.Value.OriginalFileName ?? mappingEntry.OriginalFileName);
}
}
@@ -124,20 +157,27 @@ public SourceMap ApplySourceMap(SourceMap submap, string? sourceFile = null)
var originalFileName = newMappingEntry.OriginalFileName;
var originalName = newMappingEntry.OriginalName;
- if (originalFileName != null && !newSourceMap.Sources.Contains(originalFileName))
+ if (originalFileName != null)
{
- newSourceMap.Sources.Add(originalFileName);
+ sources.Add(originalFileName);
}
- if (originalName != null && !newSourceMap.Names.Contains(originalName))
+ if (originalName != null)
{
- newSourceMap.Names.Add(originalName);
+ names.Add(originalName);
}
- newSourceMap.ParsedMappings.Add(newMappingEntry);
+ parsedMappings.Add(newMappingEntry);
}
- return newSourceMap;
+ return new SourceMap(
+ Version,
+ File,
+ null,
+ sources.ToList(),
+ names.ToList(),
+ parsedMappings,
+ new List());
}
///
@@ -146,26 +186,22 @@ public SourceMap ApplySourceMap(SourceMap submap, string? sourceFile = null)
///
/// The location in generated code for which we want to discover a mapping entry
/// A mapping entry that is a close match for the desired generated code location
- public virtual MappingEntry? GetMappingEntryForGeneratedSourcePosition(SourcePosition? generatedSourcePosition)
+ public virtual MappingEntry? GetMappingEntryForGeneratedSourcePosition(SourcePosition generatedSourcePosition)
{
- if (generatedSourcePosition == null)
- {
- return null;
- }
-
var mappingEntryToFind = new MappingEntry(generatedSourcePosition);
- var index = ParsedMappings.BinarySearch(mappingEntryToFind,
- Comparer.Create((a, b) => a.GeneratedSourcePosition.CompareTo(b.GeneratedSourcePosition)));
+ var index = ParsedMappings.BinarySearch(mappingEntryToFind, _comparer);
// If we didn't get an exact match, let's try to return the closest piece of code to the given line
if (index < 0)
{
// The BinarySearch method returns the bitwise complement of the nearest element that is larger than the desired element when there isn't a match.
// Based on tests with source maps generated with the Closure Compiler, we should consider the closest source position that is smaller than the target value when we don't have a match.
- if (~index - 1 >= 0 && ParsedMappings[~index - 1].GeneratedSourcePosition.IsEqualish(generatedSourcePosition))
+ var correctIndex = ~index - 1;
+
+ if (correctIndex >= 0 && ParsedMappings[correctIndex].GeneratedSourcePosition.IsEqualish(generatedSourcePosition))
{
- index = ~index - 1;
+ index = correctIndex;
}
}
diff --git a/src/SourceMapTools/SourcemapParser/SourceMapGenerator.cs b/src/SourceMapTools/SourcemapParser/SourceMapGenerator.cs
index 281e5ff..19a4797 100644
--- a/src/SourceMapTools/SourcemapParser/SourceMapGenerator.cs
+++ b/src/SourceMapTools/SourcemapParser/SourceMapGenerator.cs
@@ -7,6 +7,9 @@
namespace SourcemapToolkit.SourcemapParser
{
+ ///
+ /// Provides utility methods for source map serialization.
+ ///
public static class SourceMapGenerator
{
///
@@ -31,15 +34,7 @@ public static string SerializeMapping(SourceMap sourceMap, JsonSerializerOptions
throw new ArgumentNullException(nameof(sourceMap));
}
- var mapToSerialize = new SourceMap()
- {
- File = sourceMap.File,
- Names = sourceMap.Names,
- Sources = sourceMap.Sources,
- Version = sourceMap.Version,
- SourcesContent = sourceMap.SourcesContent
- };
-
+ string? mappings = null;
if (sourceMap.ParsedMappings.Count > 0)
{
var state = new MappingGenerateState(sourceMap.Names ?? new List(), sourceMap.Sources ?? new List());
@@ -52,9 +47,18 @@ public static string SerializeMapping(SourceMap sourceMap, JsonSerializerOptions
output.Append(';');
- mapToSerialize.Mappings = output.ToString();
+ mappings = output.ToString();
}
+ var mapToSerialize = new SourceMap(
+ sourceMap.Version,
+ sourceMap.File,
+ mappings,
+ sourceMap.Sources,
+ sourceMap.Names,
+ null!,
+ sourceMap.SourcesContent);
+
return JsonSerializer.Serialize(mapToSerialize,
jsonSerializerSettings ?? new JsonSerializerOptions
{
@@ -76,8 +80,7 @@ internal static void SerializeMappingEntry(MappingEntry entry, MappingGenerateSt
// Each line of generated code is separated using semicolons
while (entry.GeneratedSourcePosition.Line != state.LastGeneratedPosition.Line)
{
- state.LastGeneratedPosition.Line++;
- state.LastGeneratedPosition.Column = 0;
+ state.AdvanceLastGeneratedPositionLine();
state.IsFirstSegment = true;
output.Append(';');
}
@@ -115,7 +118,7 @@ internal static void SerializeMappingEntry(MappingEntry entry, MappingGenerateSt
*/
Base64VlqEncoder.Encode(output, entry.GeneratedSourcePosition.Column - state.LastGeneratedPosition.Column);
- state.LastGeneratedPosition.Column = entry.GeneratedSourcePosition.Column;
+ state.UpdateLastGeneratedPositionColumn(entry.GeneratedSourcePosition.Column);
if (entry.OriginalFileName != null)
{
@@ -129,10 +132,10 @@ internal static void SerializeMappingEntry(MappingEntry entry, MappingGenerateSt
state.LastSourceIndex = sourceIndex;
Base64VlqEncoder.Encode(output, entry.OriginalSourcePosition!.Line - state.LastOriginalPosition.Line);
- state.LastOriginalPosition.Line = entry.OriginalSourcePosition.Line;
Base64VlqEncoder.Encode(output, entry.OriginalSourcePosition.Column - state.LastOriginalPosition.Column);
- state.LastOriginalPosition.Column = entry.OriginalSourcePosition.Column;
+
+ state.LastOriginalPosition = entry.OriginalSourcePosition;
if (entry.OriginalName != null)
{
diff --git a/src/SourceMapTools/SourcemapParser/SourceMapParser.cs b/src/SourceMapTools/SourcemapParser/SourceMapParser.cs
index f71f7f1..a35c941 100644
--- a/src/SourceMapTools/SourcemapParser/SourceMapParser.cs
+++ b/src/SourceMapTools/SourcemapParser/SourceMapParser.cs
@@ -4,6 +4,9 @@
namespace SourcemapToolkit.SourcemapParser
{
+ ///
+ /// Source map parser.
+ ///
public static class SourceMapParser
{
///
@@ -22,7 +25,20 @@ public static class SourceMapParser
var result = JsonSerializer.DeserializeAsync(sourceMapStream).AsTask().GetAwaiter().GetResult();
if (result != null)
{
- result.ParsedMappings = MappingsListParser.ParseMappings(result.Mappings ?? string.Empty, result.Names ?? new List(), result.Sources ?? new List());
+ // Since SourceMap is immutable we need to allocate a new one and copy over all the information
+ var parsedMappings = MappingsListParser.ParseMappings(result.Mappings ?? string.Empty, result.Names ?? new List(), result.Sources ?? new List());
+
+ // Resize to free unused memory
+ parsedMappings.Capacity = parsedMappings.Count;
+
+ result = new SourceMap(
+ result.Version,
+ result.File,
+ result.Mappings,
+ result.Sources,
+ result.Names,
+ parsedMappings,
+ result.SourcesContent);
}
return result;
diff --git a/src/SourceMapTools/SourcemapParser/SourceMapTransformer.cs b/src/SourceMapTools/SourcemapParser/SourceMapTransformer.cs
index 02251f6..21a9cd3 100644
--- a/src/SourceMapTools/SourcemapParser/SourceMapTransformer.cs
+++ b/src/SourceMapTools/SourcemapParser/SourceMapTransformer.cs
@@ -2,6 +2,9 @@
namespace SourcemapToolkit.SourcemapParser
{
+ ///
+ /// Helper to compress source map by removing entries, mapped to same line.
+ ///
public static class SourceMapTransformer
{
///
@@ -12,33 +15,31 @@ public static class SourceMapTransformer
///
public static SourceMap Flatten(SourceMap sourceMap)
{
- var newMap = new SourceMap
- {
- File = sourceMap.File,
- Version = sourceMap.Version,
- Mappings = sourceMap.Mappings,
- Sources = sourceMap.Sources == null ? null : new List(sourceMap.Sources),
- SourcesContent = sourceMap.SourcesContent == null ? null : new List(sourceMap.SourcesContent),
- Names = sourceMap.Names == null ? null : new List(sourceMap.Names)
- };
-
var visitedLines = new HashSet();
+ var parsedMappings = new List(sourceMap.ParsedMappings.Count); // assume each line will not have been visited before
foreach (var mapping in sourceMap.ParsedMappings)
{
var generatedLine = mapping.GeneratedSourcePosition.Line;
- if (!visitedLines.Contains(generatedLine))
+ if (visitedLines.Add(generatedLine))
{
- visitedLines.Add(generatedLine);
- var newMapping = mapping.Clone();
- newMapping.GeneratedSourcePosition.Column = 0;
- newMapping.OriginalSourcePosition!.Column = 0;
- newMap.ParsedMappings.Add(newMapping);
+ var newMapping = mapping.CloneWithResetColumnNumber();
+ parsedMappings.Add(newMapping);
}
}
- return newMap;
+ // Free-up any unneeded space. This no-ops if we're already the right size.
+ parsedMappings.Capacity = parsedMappings.Count;
+
+ return new SourceMap(
+ sourceMap.Version,
+ sourceMap.File,
+ sourceMap.Mappings,
+ sourceMap.Sources,
+ sourceMap.Names,
+ parsedMappings,
+ sourceMap.SourcesContent);
}
}
}
diff --git a/src/SourceMapTools/SourcemapParser/SourcePosition.cs b/src/SourceMapTools/SourcemapParser/SourcePosition.cs
index 04b614c..0ee7a0c 100644
--- a/src/SourceMapTools/SourcemapParser/SourcePosition.cs
+++ b/src/SourceMapTools/SourcemapParser/SourcePosition.cs
@@ -3,31 +3,46 @@
namespace SourcemapToolkit.SourcemapParser
{
///
- /// Identifies the location of a piece of code in a JavaScript file.
+ /// Identifies the location of a piece of code in a JavaScript file
///
- public class SourcePosition : IComparable
+ public struct SourcePosition : IComparable, IEquatable
{
///
- /// Gets zero-based position line number.
+ /// Unresolved stack frame postion token.
///
- public int Line { get; internal set; }
+ public static readonly SourcePosition NotFound = new SourcePosition(-1, -1);
///
- /// Gets zero-based position column number.
+ /// Zero-based position line number.
///
- public int Column { get; internal set; }
-
- internal SourcePosition()
- : this(0, 0)
- {
- }
+ public readonly int Line;
+ ///
+ /// Zero-based position column number.
+ ///
+ public readonly int Column;
+ ///
+ /// Creates position instance with specified coordinates.
+ ///
+ /// Zero-based position line number.
+ /// Zero-based position column number.
public SourcePosition(int line, int column)
{
Line = line;
Column = column;
}
+ ///
+ /// Compares current position instance with another position.
+ ///
+ /// Position to compare to.
+ ///
+ ///
+ /// - -1: current position located before other position
+ /// - 0: both positions are the same
+ /// - 1: current position located after other position
+ ///
+ ///
public int CompareTo(SourcePosition other)
{
if (Line == other.Line)
@@ -38,9 +53,106 @@ public int CompareTo(SourcePosition other)
return Line.CompareTo(other.Line);
}
- public static bool operator <(SourcePosition x, SourcePosition y) => x.CompareTo(y) < 0;
+ ///
+ /// Checks if left position is going before right position.
+ ///
+ /// left position.
+ /// right position.
+ ///
+ ///
+ /// - true: left postion goes before right position.
+ /// - false: left postion is same or goes after right position.
+ ///
+ ///
+ public static bool operator <(SourcePosition x, SourcePosition y)
+ {
+ return x.CompareTo(y) < 0;
+ }
- public static bool operator >(SourcePosition x, SourcePosition y) => x.CompareTo(y) > 0;
+ ///
+ /// Checks if left position is going after right position.
+ ///
+ /// left position.
+ /// right position.
+ ///
+ ///
+ /// - true: left postion goes after right position.
+ /// - false: left postion is same or goes before right position.
+ ///
+ ///
+ public static bool operator >(SourcePosition x, SourcePosition y)
+ {
+ return x.CompareTo(y) > 0;
+ }
+
+ ///
+ /// Checks if both positions are the same.
+ ///
+ /// left position.
+ /// right position.
+ ///
+ ///
+ /// - true: both positions are the same.
+ /// - false: positions differ from each other.
+ ///
+ ///
+ public static bool operator ==(SourcePosition x, SourcePosition y)
+ {
+ return x.Equals(y);
+ }
+
+ ///
+ /// Checks if both positions are not the same.
+ ///
+ /// left position.
+ /// right position.
+ ///
+ ///
+ /// - true: both positions are not the the same.
+ /// - false: positions are the same.
+ ///
+ ///
+ public static bool operator !=(SourcePosition x, SourcePosition y)
+ {
+ return !x.Equals(y);
+ }
+
+ ///
+ /// Compares current postion with other one.
+ ///
+ /// Position to compare with.
+ ///
+ ///
+ /// - true: both positions are the same.
+ /// - false: positions differ from each other.
+ ///
+ ///
+ public bool Equals(SourcePosition other)
+ {
+ return Line == other.Line
+ && Column == other.Column;
+ }
+
+ ///
+ /// Compares current postion with other one.
+ ///
+ /// Position to compare with.
+ ///
+ ///
+ /// - true: both positions are the same.
+ /// - false: positions differ from each other oth is not an instance of .
+ ///
+ ///
+ public override bool Equals(object obj)
+ {
+ return (obj is SourcePosition otherSourcePosition) && Equals(otherSourcePosition);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return Column.GetHashCode() ^ Column.GetHashCode();
+ }
///
/// Returns true if we think that the two source positions are close enough together that they may in fact be the referring to the same piece of code.
@@ -68,7 +180,5 @@ public bool IsEqualish(SourcePosition other)
return false;
}
-
- public SourcePosition Clone() => new SourcePosition(Line, Column);
}
}
diff --git a/src/SourceMapTools/SourcemapParser/StringExtensions.cs b/src/SourceMapTools/SourcemapParser/StringExtensions.cs
new file mode 100644
index 0000000..8c13c4c
--- /dev/null
+++ b/src/SourceMapTools/SourcemapParser/StringExtensions.cs
@@ -0,0 +1,40 @@
+namespace SourcemapToolkit.SourcemapParser
+{
+ internal static class StringExtensions
+ {
+ ///
+ /// String.Split() allocates an O(input.Length) int array, and
+ /// is surprisingly expensive. For most cases this implementation
+ /// is faster and does fewer allocations.
+ ///
+ public static string[] SplitFast(this string input, char delimiter)
+ {
+ var segmentCount = 1;
+ for (var i = 0; i < input.Length; i++)
+ {
+ if (input[i] == delimiter)
+ {
+ segmentCount++;
+ }
+ }
+
+ var segmentId = 0;
+ var prevDelimiter = 0;
+ var segments = new string[segmentCount];
+
+ for (var i = 0; i < input.Length; i++)
+ {
+ if (input[i] == delimiter)
+ {
+ segments[segmentId] = input.Substring(prevDelimiter, i - prevDelimiter);
+ segmentId++;
+ prevDelimiter = i + 1;
+ }
+ }
+
+ segments[segmentId] = input.Substring(prevDelimiter);
+
+ return segments;
+ }
+ }
+}
diff --git a/src/SourceMapTools/SourcemapTools.csproj b/src/SourceMapTools/SourcemapTools.csproj
index d1ff234..f06e96e 100644
--- a/src/SourceMapTools/SourcemapTools.csproj
+++ b/src/SourceMapTools/SourcemapTools.csproj
@@ -1,6 +1,10 @@
+
+ bin\$(Configuration)\$(TargetFramework)\SourcemapTools.xml
+
+
diff --git a/tests/SourcemapTools.UnitTests/CallstackDeminifier/FunctionMapConsumerUnitTests.cs b/tests/SourcemapTools.UnitTests/CallstackDeminifier/FunctionMapConsumerUnitTests.cs
index 7471325..a15c742 100644
--- a/tests/SourcemapTools.UnitTests/CallstackDeminifier/FunctionMapConsumerUnitTests.cs
+++ b/tests/SourcemapTools.UnitTests/CallstackDeminifier/FunctionMapConsumerUnitTests.cs
@@ -31,6 +31,7 @@ public void GetWrappingFunctionForSourceLocation_SingleIrrelevantFunctionMapEntr
{
new FunctionMapEntry(
null!,
+ null,
new SourcePosition(40, 10),
new SourcePosition(50, 10))
};
@@ -50,6 +51,7 @@ public void GetWrappingFunctionForSourceLocation_SingleRelevantFunctionMapEntry_
var sourcePosition = new SourcePosition(41, 2);
var functionMapEntry = new FunctionMapEntry(
null!,
+ null,
new SourcePosition(40, 10),
new SourcePosition(50, 10));
@@ -73,11 +75,13 @@ public void GetWrappingFunctionForSourceLocation_MultipleFunctionMapEntriesSingl
var sourcePosition = new SourcePosition(31, 0);
var functionMapEntry = new FunctionMapEntry(
null!,
+ null,
new SourcePosition(10, 10),
new SourcePosition(20, 30));
var functionMapEntry2 = new FunctionMapEntry(
null!,
+ null,
new SourcePosition(30, 0),
new SourcePosition(40, 2));
@@ -102,11 +106,13 @@ public void GetWrappingFunctionForSourceLocation_MultipleFunctionMapEntriesMulti
var sourcePosition = new SourcePosition(10, 25);
var functionMapEntry = new FunctionMapEntry(
null!,
+ null,
new SourcePosition(5, 10),
new SourcePosition(20, 30));
var functionMapEntry2 = new FunctionMapEntry(
null!,
+ null,
new SourcePosition(9, 0),
new SourcePosition(15, 2));
diff --git a/tests/SourcemapTools.UnitTests/CallstackDeminifier/FunctionMapGeneratorUnitTests.cs b/tests/SourcemapTools.UnitTests/CallstackDeminifier/FunctionMapGeneratorUnitTests.cs
index 1bbb826..4cec508 100644
--- a/tests/SourcemapTools.UnitTests/CallstackDeminifier/FunctionMapGeneratorUnitTests.cs
+++ b/tests/SourcemapTools.UnitTests/CallstackDeminifier/FunctionMapGeneratorUnitTests.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using Moq;
using SourcemapToolkit.SourcemapParser;
using SourcemapToolkit.SourcemapParser.UnitTests;
using Xunit;
@@ -32,7 +31,7 @@ public void ParseSourceCode_NoFunctionsInSource_EmptyFunctionList()
var sourceCode = "bar();";
// Act
- var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode));
+ var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode), CreateSourceMapMock());
// Assert
Assert.Empty(functionMap);
@@ -45,17 +44,17 @@ public void ParseSourceCode_SingleLineFunctionInSource_CorrectZeroBasedColumnNum
var sourceCode = "function foo(){bar();}";
// Act
- var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode));
+ var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode), CreateSourceMapMock());
// Assert
Assert.Single(functionMap);
Assert.Equal("foo", functionMap[0].Bindings[0].Name);
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.Line);
Assert.Equal(9, functionMap[0].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[0].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[0].EndSourcePosition.Line);
- Assert.Equal(14, functionMap[0].StartSourcePosition.Column);
- Assert.Equal(22, functionMap[0].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[0].Start.Line);
+ Assert.Equal(0, functionMap[0].End.Line);
+ Assert.Equal(14, functionMap[0].Start.Column);
+ Assert.Equal(22, functionMap[0].End.Column);
}
[Fact]
@@ -66,17 +65,17 @@ public void ParseSourceCode_MultiLineFunctionInSource_CorrectColumnAndZeroBasedL
Environment.NewLine + "}";
// Act
- var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode));
+ var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode), CreateSourceMapMock());
// Assert
Assert.Single(functionMap);
Assert.Equal("foo", functionMap[0].Bindings[0].Name);
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.Line);
Assert.Equal(9, functionMap[0].Bindings[0].SourcePosition.Column);
- Assert.Equal(1, functionMap[0].StartSourcePosition.Line);
- Assert.Equal(3, functionMap[0].EndSourcePosition.Line);
- Assert.Equal(0, functionMap[0].StartSourcePosition.Column);
- Assert.Equal(1, functionMap[0].EndSourcePosition.Column);
+ Assert.Equal(1, functionMap[0].Start.Line);
+ Assert.Equal(3, functionMap[0].End.Line);
+ Assert.Equal(0, functionMap[0].Start.Column);
+ Assert.Equal(1, functionMap[0].End.Column);
}
[Fact]
@@ -86,7 +85,7 @@ public void ParseSourceCode_TwoSingleLineFunctions_TwoFunctionMapEntries()
var sourceCode = "function foo(){bar();}function bar(){baz();}";
// Act
- var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode));
+ var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode), CreateSourceMapMock());
// Assert
Assert.Equal(2, functionMap.Count);
@@ -94,18 +93,18 @@ public void ParseSourceCode_TwoSingleLineFunctions_TwoFunctionMapEntries()
Assert.Equal("bar", functionMap[0].Bindings[0].Name);
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.Line);
Assert.Equal(31, functionMap[0].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[0].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[0].EndSourcePosition.Line);
- Assert.Equal(36, functionMap[0].StartSourcePosition.Column);
- Assert.Equal(44, functionMap[0].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[0].Start.Line);
+ Assert.Equal(0, functionMap[0].End.Line);
+ Assert.Equal(36, functionMap[0].Start.Column);
+ Assert.Equal(44, functionMap[0].End.Column);
Assert.Equal("foo", functionMap[1].Bindings[0].Name);
Assert.Equal(0, functionMap[1].Bindings[0].SourcePosition.Line);
Assert.Equal(9, functionMap[1].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[1].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[1].EndSourcePosition.Line);
- Assert.Equal(14, functionMap[1].StartSourcePosition.Column);
- Assert.Equal(22, functionMap[1].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[1].Start.Line);
+ Assert.Equal(0, functionMap[1].End.Line);
+ Assert.Equal(14, functionMap[1].Start.Column);
+ Assert.Equal(22, functionMap[1].End.Column);
}
[Fact]
@@ -115,7 +114,7 @@ public void ParseSourceCode_TwoNestedSingleLineFunctions_TwoFunctionMapEntries()
var sourceCode = "function foo(){function bar(){baz();}}";
// Act
- var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode));
+ var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode), CreateSourceMapMock());
// Assert
Assert.Equal(2, functionMap.Count);
@@ -123,18 +122,18 @@ public void ParseSourceCode_TwoNestedSingleLineFunctions_TwoFunctionMapEntries()
Assert.Equal("bar", functionMap[0].Bindings[0].Name);
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.Line);
Assert.Equal(24, functionMap[0].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[0].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[0].EndSourcePosition.Line);
- Assert.Equal(29, functionMap[0].StartSourcePosition.Column);
- Assert.Equal(37, functionMap[0].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[0].Start.Line);
+ Assert.Equal(0, functionMap[0].End.Line);
+ Assert.Equal(29, functionMap[0].Start.Column);
+ Assert.Equal(37, functionMap[0].End.Column);
Assert.Equal("foo", functionMap[1].Bindings[0].Name);
Assert.Equal(0, functionMap[1].Bindings[0].SourcePosition.Line);
Assert.Equal(9, functionMap[1].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[1].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[1].EndSourcePosition.Line);
- Assert.Equal(14, functionMap[1].StartSourcePosition.Column);
- Assert.Equal(38, functionMap[1].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[1].Start.Line);
+ Assert.Equal(0, functionMap[1].End.Line);
+ Assert.Equal(14, functionMap[1].Start.Column);
+ Assert.Equal(38, functionMap[1].End.Column);
}
[Fact]
@@ -144,7 +143,7 @@ public void ParseSourceCode_FunctionAssignedToVariable_FunctionMapEntryGenerated
var sourceCode = "var foo = function(){bar();}";
// Act
- var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode));
+ var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode), CreateSourceMapMock());
// Assert
Assert.Single(functionMap);
@@ -152,10 +151,10 @@ public void ParseSourceCode_FunctionAssignedToVariable_FunctionMapEntryGenerated
Assert.Equal("foo", functionMap[0].Bindings[0].Name);
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.Line);
Assert.Equal(4, functionMap[0].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[0].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[0].EndSourcePosition.Line);
- Assert.Equal(20, functionMap[0].StartSourcePosition.Column);
- Assert.Equal(28, functionMap[0].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[0].Start.Line);
+ Assert.Equal(0, functionMap[0].End.Line);
+ Assert.Equal(20, functionMap[0].Start.Column);
+ Assert.Equal(28, functionMap[0].End.Column);
}
[Fact]
@@ -165,7 +164,7 @@ public void ParseSourceCode_StaticMethod_FunctionMapEntryGenerated()
var sourceCode = "var foo = function(){};foo.bar = function() { baz(); }";
// Act
- var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode));
+ var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode), CreateSourceMapMock());
// Assert
Assert.Equal(2, functionMap.Count);
@@ -174,18 +173,18 @@ public void ParseSourceCode_StaticMethod_FunctionMapEntryGenerated()
Assert.Equal("bar", functionMap[0].Bindings[1].Name);
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.Line);
Assert.Equal(23, functionMap[0].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[0].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[0].EndSourcePosition.Line);
- Assert.Equal(44, functionMap[0].StartSourcePosition.Column);
- Assert.Equal(54, functionMap[0].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[0].Start.Line);
+ Assert.Equal(0, functionMap[0].End.Line);
+ Assert.Equal(44, functionMap[0].Start.Column);
+ Assert.Equal(54, functionMap[0].End.Column);
Assert.Equal("foo", functionMap[1].Bindings[0].Name);
Assert.Equal(0, functionMap[1].Bindings[0].SourcePosition.Line);
Assert.Equal(4, functionMap[1].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[1].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[1].EndSourcePosition.Line);
- Assert.Equal(20, functionMap[1].StartSourcePosition.Column);
- Assert.Equal(22, functionMap[1].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[1].Start.Line);
+ Assert.Equal(0, functionMap[1].End.Line);
+ Assert.Equal(20, functionMap[1].Start.Column);
+ Assert.Equal(22, functionMap[1].End.Column);
}
[Fact]
@@ -195,7 +194,7 @@ public void ParseSourceCode_InstanceMethod_FunctionMapEntryGenerated()
var sourceCode = "var foo = function(){}; foo.prototype.bar = function () { baz(); }";
// Act
- var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode));
+ var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode), CreateSourceMapMock());
// Assert
Assert.Equal(2, functionMap.Count);
@@ -205,18 +204,18 @@ public void ParseSourceCode_InstanceMethod_FunctionMapEntryGenerated()
Assert.Equal("bar", functionMap[0].Bindings[2].Name);
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.Line);
Assert.Equal(24, functionMap[0].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[0].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[0].EndSourcePosition.Line);
- Assert.Equal(56, functionMap[0].StartSourcePosition.Column);
- Assert.Equal(66, functionMap[0].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[0].Start.Line);
+ Assert.Equal(0, functionMap[0].End.Line);
+ Assert.Equal(56, functionMap[0].Start.Column);
+ Assert.Equal(66, functionMap[0].End.Column);
Assert.Equal("foo", functionMap[1].Bindings[0].Name);
Assert.Equal(0, functionMap[1].Bindings[0].SourcePosition.Line);
Assert.Equal(4, functionMap[1].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[1].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[1].EndSourcePosition.Line);
- Assert.Equal(20, functionMap[1].StartSourcePosition.Column);
- Assert.Equal(22, functionMap[1].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[1].Start.Line);
+ Assert.Equal(0, functionMap[1].End.Line);
+ Assert.Equal(20, functionMap[1].Start.Column);
+ Assert.Equal(22, functionMap[1].End.Column);
}
[Fact]
@@ -226,7 +225,7 @@ public void ParseSourceCode_InstanceMethodInObjectInitializer_FunctionMapEntryGe
var sourceCode = "var foo = function(){}; foo.prototype = { bar: function () { baz(); } }";
// Act
- var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode));
+ var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode), CreateSourceMapMock());
// Assert
Assert.Equal(2, functionMap.Count);
@@ -238,18 +237,18 @@ public void ParseSourceCode_InstanceMethodInObjectInitializer_FunctionMapEntryGe
Assert.Equal("bar", functionMap[0].Bindings[2].Name);
Assert.Equal(0, functionMap[0].Bindings[2].SourcePosition.Line);
Assert.Equal(42, functionMap[0].Bindings[2].SourcePosition.Column);
- Assert.Equal(0, functionMap[0].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[0].EndSourcePosition.Line);
- Assert.Equal(59, functionMap[0].StartSourcePosition.Column);
- Assert.Equal(69, functionMap[0].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[0].Start.Line);
+ Assert.Equal(0, functionMap[0].End.Line);
+ Assert.Equal(59, functionMap[0].Start.Column);
+ Assert.Equal(69, functionMap[0].End.Column);
Assert.Equal("foo", functionMap[1].Bindings[0].Name);
Assert.Equal(0, functionMap[1].Bindings[0].SourcePosition.Line);
Assert.Equal(4, functionMap[1].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[1].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[1].EndSourcePosition.Line);
- Assert.Equal(20, functionMap[1].StartSourcePosition.Column);
- Assert.Equal(22, functionMap[1].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[1].Start.Line);
+ Assert.Equal(0, functionMap[1].End.Line);
+ Assert.Equal(20, functionMap[1].Start.Column);
+ Assert.Equal(22, functionMap[1].End.Column);
}
[Fact]
@@ -259,7 +258,7 @@ public void ParseSourceCode_FunctionAssignedToVariableAndHasName_FunctionMapEntr
var sourceCode = "var foo = function myCoolFunctionName(){ bar(); }";
// Act
- var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode));
+ var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode), CreateSourceMapMock());
// Assert
Assert.Single(functionMap);
@@ -267,10 +266,10 @@ public void ParseSourceCode_FunctionAssignedToVariableAndHasName_FunctionMapEntr
Assert.Equal("foo", functionMap[0].Bindings[0].Name);
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.Line);
Assert.Equal(4, functionMap[0].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[0].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[0].EndSourcePosition.Line);
- Assert.Equal(39, functionMap[0].StartSourcePosition.Column);
- Assert.Equal(49, functionMap[0].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[0].Start.Line);
+ Assert.Equal(0, functionMap[0].End.Line);
+ Assert.Equal(39, functionMap[0].Start.Column);
+ Assert.Equal(49, functionMap[0].End.Column);
}
[Fact]
@@ -280,7 +279,7 @@ public void ParseSourceCode_StaticMethodAndFunctionHasName_FunctionMapEntryGener
var sourceCode = "var foo = function(){};foo.bar = function myCoolFunctionName() { baz(); }";
// Act
- var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode));
+ var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode), CreateSourceMapMock());
// Assert
Assert.Equal(2, functionMap.Count);
@@ -289,18 +288,18 @@ public void ParseSourceCode_StaticMethodAndFunctionHasName_FunctionMapEntryGener
Assert.Equal("bar", functionMap[0].Bindings[1].Name);
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.Line);
Assert.Equal(23, functionMap[0].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[0].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[0].EndSourcePosition.Line);
- Assert.Equal(63, functionMap[0].StartSourcePosition.Column);
- Assert.Equal(73, functionMap[0].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[0].Start.Line);
+ Assert.Equal(0, functionMap[0].End.Line);
+ Assert.Equal(63, functionMap[0].Start.Column);
+ Assert.Equal(73, functionMap[0].End.Column);
Assert.Equal("foo", functionMap[1].Bindings[0].Name);
Assert.Equal(0, functionMap[1].Bindings[0].SourcePosition.Line);
Assert.Equal(4, functionMap[1].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[1].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[1].EndSourcePosition.Line);
- Assert.Equal(20, functionMap[1].StartSourcePosition.Column);
- Assert.Equal(22, functionMap[1].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[1].Start.Line);
+ Assert.Equal(0, functionMap[1].End.Line);
+ Assert.Equal(20, functionMap[1].Start.Column);
+ Assert.Equal(22, functionMap[1].End.Column);
}
[Fact]
@@ -310,7 +309,7 @@ public void ParseSourceCode_InstanceMethodAndFunctionHasName_FunctionMapEntryGen
var sourceCode = "var foo = function(){}; foo.prototype.bar = function myCoolFunctionName() { baz(); }";
// Act
- var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode));
+ var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode), CreateSourceMapMock());
// Assert
Assert.Equal(2, functionMap.Count);
@@ -320,18 +319,18 @@ public void ParseSourceCode_InstanceMethodAndFunctionHasName_FunctionMapEntryGen
Assert.Equal("bar", functionMap[0].Bindings[2].Name);
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.Line);
Assert.Equal(24, functionMap[0].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[0].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[0].EndSourcePosition.Line);
- Assert.Equal(74, functionMap[0].StartSourcePosition.Column);
- Assert.Equal(84, functionMap[0].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[0].Start.Line);
+ Assert.Equal(0, functionMap[0].End.Line);
+ Assert.Equal(74, functionMap[0].Start.Column);
+ Assert.Equal(84, functionMap[0].End.Column);
Assert.Equal("foo", functionMap[1].Bindings[0].Name);
Assert.Equal(0, functionMap[1].Bindings[0].SourcePosition.Line);
Assert.Equal(4, functionMap[1].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[1].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[1].EndSourcePosition.Line);
- Assert.Equal(20, functionMap[1].StartSourcePosition.Column);
- Assert.Equal(22, functionMap[1].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[1].Start.Line);
+ Assert.Equal(0, functionMap[1].End.Line);
+ Assert.Equal(20, functionMap[1].Start.Column);
+ Assert.Equal(22, functionMap[1].End.Column);
}
[Fact]
@@ -341,7 +340,7 @@ public void ParseSourceCode_InstanceMethodWithObjectInitializerAndFunctionHasNam
var sourceCode = "var foo = function(){}; foo.prototype = { bar: function myCoolFunctionName() { baz(); } }";
// Act
- var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode));
+ var functionMap = FunctionMapGenerator.ParseSourceCode(UnitTestUtils.StreamFromString(sourceCode), CreateSourceMapMock());
// Assert
Assert.Equal(2, functionMap.Count);
@@ -353,172 +352,30 @@ public void ParseSourceCode_InstanceMethodWithObjectInitializerAndFunctionHasNam
Assert.Equal("bar", functionMap[0].Bindings[2].Name);
Assert.Equal(0, functionMap[0].Bindings[2].SourcePosition.Line);
Assert.Equal(42, functionMap[0].Bindings[2].SourcePosition.Column);
- Assert.Equal(0, functionMap[0].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[0].EndSourcePosition.Line);
- Assert.Equal(77, functionMap[0].StartSourcePosition.Column);
- Assert.Equal(87, functionMap[0].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[0].Start.Line);
+ Assert.Equal(0, functionMap[0].End.Line);
+ Assert.Equal(77, functionMap[0].Start.Column);
+ Assert.Equal(87, functionMap[0].End.Column);
Assert.Equal("foo", functionMap[1].Bindings[0].Name);
Assert.Equal(0, functionMap[1].Bindings[0].SourcePosition.Line);
Assert.Equal(4, functionMap[1].Bindings[0].SourcePosition.Column);
- Assert.Equal(0, functionMap[1].StartSourcePosition.Line);
- Assert.Equal(0, functionMap[1].EndSourcePosition.Line);
- Assert.Equal(20, functionMap[1].StartSourcePosition.Column);
- Assert.Equal(22, functionMap[1].EndSourcePosition.Column);
+ Assert.Equal(0, functionMap[1].Start.Line);
+ Assert.Equal(0, functionMap[1].End.Line);
+ Assert.Equal(20, functionMap[1].Start.Column);
+ Assert.Equal(22, functionMap[1].End.Column);
}
- [Fact]
- public void GetDeminifiedMethodNameFromSourceMap_NullFunctionMapEntry_ThrowsException()
- {
- // Arrange
- FunctionMapEntry? functionMapEntry = null;
- var sourceMap = new Mock().Object;
-
- // Act
- Assert.Throws(() => FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry!, sourceMap));
- }
-
- [Fact]
- public void GetDeminifiedMethodNameFromSourceMap_NullSourceMap_ThrowsException()
- {
- // Arrange
- var functionMapEntry = new FunctionMapEntry(null!, null!, null!);
- SourceMap? sourceMap = null;
-
- // Act
- Assert.Throws(() => FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap!));
- }
-
- [Fact]
- public void GetDeminifiedMethodNameFromSourceMap_NoBinding_ReturnNullMethodName()
+ private static SourceMap CreateSourceMapMock()
{
- // Arrange
- var functionMapEntry = new FunctionMapEntry(null!, null!, null!);
- var sourceMap = new Mock().Object;
-
- // Act
- var result = FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap);
-
- // Assert
- Assert.Null(result);
- }
-
- [Fact]
- public void GetDeminifiedMethodNameFromSourceMap_HasSingleBindingNoMatchingMapping_ReturnNullMethodName()
- {
- // Arrange
- var functionMapEntry = new FunctionMapEntry(
- new List
- {
- new BindingInformation(
- null!,
- new SourcePosition(20, 15))
- }, null!, null!);
-
- var sourceMap = new Mock();
- sourceMap.Setup(x => x.GetMappingEntryForGeneratedSourcePosition(It.IsAny())).Returns(null);
-
- // Act
- var result = FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap.Object);
-
- // Assert
- Assert.Null(result);
- }
-
- [Fact]
- public void GetDeminifiedMethodNameFromSourceMap_HasSingleBindingMatchingMapping_ReturnsMethodName()
- {
- // Arrange
- var functionMapEntry = new FunctionMapEntry(
- new List
- {
- new BindingInformation(
- null!,
- new SourcePosition(5, 8))
- }, null!, null!);
-
- var sourceMap = new Mock();
- sourceMap.Setup(
- x =>
- x.GetMappingEntryForGeneratedSourcePosition(
- It.Is(y => y.Line == 5 && y.Column == 8)))
- .Returns(new MappingEntry(null!, null, "foo", null));
-
- // Act
- var result = FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap.Object);
-
- // Assert
- Assert.Equal("foo", result);
- }
-
- [Fact]
- public void GetDeminifiedMethodNameFromSourceMap_MatchingMappingMultipleBindingsMissingPrototypeMapping_ReturnsMethodName()
- {
- // Arrange
- var functionMapEntry = new FunctionMapEntry(
- new List
- {
- new BindingInformation(
- null!,
- new SourcePosition(86, 52)),
- new BindingInformation(
- null!,
- new SourcePosition(88, 78))
- }, null!, null!);
-
- var sourceMap = new Mock();
- sourceMap.Setup(
- x =>
- x.GetMappingEntryForGeneratedSourcePosition(
- It.Is(y => y.Line == 86 && y.Column == 52)))
- .Returns(null);
-
- sourceMap.Setup(
- x =>
- x.GetMappingEntryForGeneratedSourcePosition(
- It.Is(y => y.Line == 88 && y.Column == 78)))
- .Returns(new MappingEntry(null!, null, "baz", null));
-
- // Act
- var result = FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap.Object);
-
- // Assert
- Assert.Equal("baz", result);
- }
-
- [Fact]
- public void GetDeminifiedMethodNameFromSourceMap_MatchingMappingMultipleBindings_ReturnsMethodNameWithFullBinding()
- {
- // Arrange
- var functionMapEntry = new FunctionMapEntry(
- new List
- {
- new BindingInformation(
- null!,
- new SourcePosition(5, 5)),
- new BindingInformation(
- null!,
- new SourcePosition(20, 10))
- }, null!, null!);
-
- var sourceMap = new Mock();
- sourceMap.Setup(
- x =>
- x.GetMappingEntryForGeneratedSourcePosition(
- It.Is(y => y.Line == 5 && y.Column == 5)))
- .Returns(new MappingEntry(null!, null, "bar", null));
-
- sourceMap.Setup(
- x =>
- x.GetMappingEntryForGeneratedSourcePosition(
- It.Is(y => y.Line == 20 && y.Column == 10)))
- .Returns(new MappingEntry(null!, null, "baz", null));
-
- // Act
- var result = FunctionMapGenerator.GetDeminifiedMethodNameFromSourceMap(functionMapEntry, sourceMap.Object);
-
- // Assert
- Assert.Equal("bar.baz", result);
+ return new SourceMap(
+ 0 /* version */,
+ default /* file */,
+ default /* mappings */,
+ default /* sources */,
+ default /* names */,
+ new List() /* parsedMappings */,
+ default /* sourcesContent */);
}
}
}
\ No newline at end of file
diff --git a/tests/SourcemapTools.UnitTests/CallstackDeminifier/SourceMapExtensionsUnitTests.cs b/tests/SourcemapTools.UnitTests/CallstackDeminifier/SourceMapExtensionsUnitTests.cs
new file mode 100644
index 0000000..2175585
--- /dev/null
+++ b/tests/SourcemapTools.UnitTests/CallstackDeminifier/SourceMapExtensionsUnitTests.cs
@@ -0,0 +1,153 @@
+using System.Collections.Generic;
+using Moq;
+using SourcemapToolkit.SourcemapParser;
+using Xunit;
+
+namespace SourcemapToolkit.CallstackDeminifier.UnitTests
+{
+ public class SourceMapExtensionsUnitTests
+ {
+ [Fact]
+ public void GetDeminifiedMethodName_EmptyBinding_ReturnNullMethodName()
+ {
+ // Arrange
+ var bindings = new List();
+ var sourceMap = CreateSourceMapMock();
+
+ // Act
+ var result = SourceMapExtensions.GetDeminifiedMethodName(sourceMap.Object, bindings);
+
+ // Assert
+ Assert.Null(result);
+ }
+
+ [Fact]
+ public void GetDeminifiedMethodName_HasSingleBindingNoMatchingMapping_ReturnNullMethodName()
+ {
+ // Arrange
+ var bindings = new List()
+ {
+ new BindingInformation(
+ name: default!,
+ sourcePosition: new SourcePosition(20, 15))
+ };
+
+ var sourceMap = CreateSourceMapMock();
+ sourceMap.Setup(x => x.GetMappingEntryForGeneratedSourcePosition(It.IsAny())).Returns((MappingEntry?)null);
+
+ // Act
+ var result = SourceMapExtensions.GetDeminifiedMethodName(sourceMap.Object, bindings);
+
+ // Assert
+ Assert.Null(result);
+ }
+
+ [Fact]
+ public void GetDeminifiedMethodName_HasSingleBindingMatchingMapping_ReturnsMethodName()
+ {
+ // Arrange
+ var bindings = new List()
+ {
+ new BindingInformation(
+ name: default!,
+ sourcePosition: new SourcePosition(5, 8))
+ };
+
+ var sourceMap = CreateSourceMapMock();
+ sourceMap.Setup(
+ x =>
+ x.GetMappingEntryForGeneratedSourcePosition(
+ It.Is(y => y.Line == 5 && y.Column == 8)))
+ .Returns(new MappingEntry(generatedSourcePosition: default, null, originalName: "foo", null));
+
+ // Act
+ var result = SourceMapExtensions.GetDeminifiedMethodName(sourceMap.Object, bindings);
+
+ // Assert
+ Assert.Equal("foo", result);
+ sourceMap.Verify();
+ }
+
+ [Fact]
+ public void GetDeminifiedMethodName_MatchingMappingMultipleBindingsMissingPrototypeMapping_ReturnsMethodName()
+ {
+ // Arrange
+ var bindings = new List
+ {
+ new BindingInformation(
+ name: default!,
+ sourcePosition: new SourcePosition(86, 52)),
+ new BindingInformation(
+ name: default!,
+ sourcePosition: new SourcePosition(88, 78))
+ };
+
+ var sourceMap = CreateSourceMapMock();
+ sourceMap.Setup(
+ x =>
+ x.GetMappingEntryForGeneratedSourcePosition(
+ It.Is(y => y.Line == 86 && y.Column == 52)))
+ .Returns((MappingEntry?)null);
+
+ sourceMap.Setup(
+ x =>
+ x.GetMappingEntryForGeneratedSourcePosition(
+ It.Is(y => y.Line == 88 && y.Column == 78)))
+ .Returns(new MappingEntry(default, null, "baz", null));
+
+ // Act
+ var result = SourceMapExtensions.GetDeminifiedMethodName(sourceMap.Object, bindings);
+
+ // Assert
+ Assert.Equal("baz", result);
+ sourceMap.Verify();
+ }
+
+ [Fact]
+ public void GetDeminifiedMethodName_MatchingMappingMultipleBindings_ReturnsMethodNameWithFullBinding()
+ {
+ // Arrange
+ var bindings = new List
+ {
+ new BindingInformation(
+ name: default!,
+ sourcePosition: new SourcePosition(5, 5)),
+ new BindingInformation(
+ name: default!,
+ sourcePosition: new SourcePosition(20, 10))
+ };
+
+ var sourceMap = CreateSourceMapMock();
+ sourceMap.Setup(
+ x =>
+ x.GetMappingEntryForGeneratedSourcePosition(
+ It.Is(y => y.Line == 5 && y.Column == 5)))
+ .Returns(new MappingEntry(generatedSourcePosition: default, null, originalName: "bar", null));
+
+ sourceMap.Setup(
+ x =>
+ x.GetMappingEntryForGeneratedSourcePosition(
+ It.Is(y => y.Line == 20 && y.Column == 10)))
+ .Returns(new MappingEntry(generatedSourcePosition: default, null, originalName: "baz", null));
+
+ // Act
+ var result = SourceMapExtensions.GetDeminifiedMethodName(sourceMap.Object, bindings);
+
+ // Assert
+ Assert.Equal("bar.baz", result);
+ sourceMap.Verify();
+ }
+
+ private static Mock CreateSourceMapMock()
+ {
+ return new Mock(() => new SourceMap(
+ 0 /* version */,
+ default /* file */,
+ default /* mappings */,
+ default /* sources */,
+ default /* names */,
+ new List() /* parsedMappings */,
+ default /* sourcesContent */));
+ }
+ }
+}
diff --git a/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackFrameDeminifierUnitTests.cs b/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackFrameDeminifierUnitTests.cs
index dfddac9..3b2dd82 100644
--- a/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackFrameDeminifierUnitTests.cs
+++ b/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackFrameDeminifierUnitTests.cs
@@ -60,7 +60,7 @@ public void DeminifyStackFrame_StackFrameNullProperties_DoesNotThrowException()
// Assert
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.MethodName);
- Assert.Null(stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
+ Assert.Equal(SourcePosition.NotFound, stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.FilePath);
}
@@ -82,7 +82,7 @@ public void SimpleStackFrameDeminierDeminifyStackFrame_FunctionMapReturnsNull_No
// Assert
Assert.Equal(DeminificationError.NoSourceCodeProvided, stackFrameDeminification.DeminificationError);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.MethodName);
- Assert.Null(stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
+ Assert.Equal(SourcePosition.NotFound, stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.FilePath);
}
@@ -107,7 +107,7 @@ public void SimpleStackFrameDeminierDeminifyStackFrame_GetWRappingFunctionForSou
// Assert
Assert.Equal(DeminificationError.NoWrapingFunctionFound, stackFrameDeminification.DeminificationError);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.MethodName);
- Assert.Null(stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
+ Assert.Equal(SourcePosition.NotFound, stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.FilePath);
}
@@ -116,7 +116,7 @@ public void SimpleStackFrameDeminierDeminifyStackFrame_WrapingFunctionFound_NoDe
{
// Arrange
var filePath = "foo";
- var wrapingFunctionMapEntry = new FunctionMapEntry(null!, null!, null!) {DeminfifiedMethodName = "DeminifiedFoo"};
+ var wrapingFunctionMapEntry = CreateFunctionMapEntry("DeminifiedFoo");
var stackFrame = new StackFrame(null) { FilePath = filePath };
var functionMapStore = new Mock();
functionMapStore.Setup(c => c.GetFunctionMapForSourceCode(filePath))
@@ -132,8 +132,8 @@ public void SimpleStackFrameDeminierDeminifyStackFrame_WrapingFunctionFound_NoDe
// Assert
Assert.Equal(DeminificationError.None, stackFrameDeminification.DeminificationError);
- Assert.Equal(wrapingFunctionMapEntry.DeminfifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
- Assert.Null(stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
+ Assert.Equal(wrapingFunctionMapEntry.DeminifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
+ Assert.Equal(SourcePosition.NotFound, stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.FilePath);
}
@@ -143,7 +143,7 @@ public void StackFrameDeminierDeminifyStackFrame_SourceMapProviderReturnsNull_No
{
// Arrange
var filePath = "foo";
- var wrapingFunctionMapEntry = new FunctionMapEntry(null!, null!, null!) { DeminfifiedMethodName = "DeminifiedFoo" };
+ var wrapingFunctionMapEntry = CreateFunctionMapEntry(deminifiedMethodName: "DeminifiedFoo");
var stackFrame = new StackFrame(null) { FilePath = filePath };
var functionMapStore = new Mock();
functionMapStore.Setup(c => c.GetFunctionMapForSourceCode(filePath))
@@ -159,8 +159,8 @@ public void StackFrameDeminierDeminifyStackFrame_SourceMapProviderReturnsNull_No
// Assert
Assert.Equal(DeminificationError.NoSourceMap, stackFrameDeminification.DeminificationError);
- Assert.Equal(wrapingFunctionMapEntry.DeminfifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
- Assert.Null(stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
+ Assert.Equal(wrapingFunctionMapEntry.DeminifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
+ Assert.Equal(SourcePosition.NotFound, stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.FilePath);
}
@@ -169,7 +169,7 @@ public void StackFrameDeminierDeminifyStackFrame_SourceMapParsingNull_SourceMapF
{
// Arrange
var filePath = "foo";
- var wrapingFunctionMapEntry = new FunctionMapEntry(null!, null!, null!) { DeminfifiedMethodName = "DeminifiedFoo" };
+ var wrapingFunctionMapEntry = CreateFunctionMapEntry(deminifiedMethodName: "DeminifiedFoo");
var stackFrame = new StackFrame(null) { FilePath = filePath };
var functionMapStore = new Mock();
functionMapStore.Setup(c => c.GetFunctionMapForSourceCode(filePath))
@@ -178,7 +178,7 @@ public void StackFrameDeminierDeminifyStackFrame_SourceMapParsingNull_SourceMapF
functionMapConsumer.Setup(c => c.GetWrappingFunctionForSourceLocation(It.IsAny(), It.IsAny>()))
.Returns(wrapingFunctionMapEntry);
var sourceMapStore = new Mock();
- sourceMapStore.Setup(c => c.GetSourceMapForUrl(It.IsAny())).Returns(new SourceMap());
+ sourceMapStore.Setup(c => c.GetSourceMapForUrl(It.IsAny())).Returns(CreateSourceMap());
var stackFrameDeminifier = GetStackFrameDeminifierWithMockDependencies(sourceMapStore: sourceMapStore.Object, functionMapStore: functionMapStore.Object, functionMapConsumer: functionMapConsumer.Object);
@@ -187,8 +187,8 @@ public void StackFrameDeminierDeminifyStackFrame_SourceMapParsingNull_SourceMapF
// Assert
Assert.Equal(DeminificationError.NoMatchingMapingInSourceMap, stackFrameDeminification.DeminificationError);
- Assert.Equal(wrapingFunctionMapEntry.DeminfifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
- Assert.Null(stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
+ Assert.Equal(wrapingFunctionMapEntry.DeminifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
+ Assert.Equal(SourcePosition.NotFound, stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.FilePath);
}
@@ -197,13 +197,13 @@ public void StackFrameDeminierDeminifyStackFrame_SourceMapGeneratedMappingEntryN
{
// Arrange
var filePath = "foo";
- var wrapingFunctionMapEntry = new FunctionMapEntry(null!, null!, null!) { DeminfifiedMethodName = "DeminifiedFoo" };
+ var wrapingFunctionMapEntry = CreateFunctionMapEntry(deminifiedMethodName: "DeminifiedFoo");
var stackFrame = new StackFrame(null) { FilePath = filePath };
var functionMapStore = new Mock();
functionMapStore.Setup(c => c.GetFunctionMapForSourceCode(filePath))
.Returns(new List());
var sourceMapStore = new Mock();
- var sourceMap = new SourceMap();
+ var sourceMap = CreateSourceMap(new List());
sourceMapStore.Setup(c => c.GetSourceMapForUrl(It.IsAny())).Returns(sourceMap);
var functionMapConsumer = new Mock();
@@ -217,10 +217,31 @@ public void StackFrameDeminierDeminifyStackFrame_SourceMapGeneratedMappingEntryN
// Assert
Assert.Equal(DeminificationError.NoMatchingMapingInSourceMap, stackFrameDeminification.DeminificationError);
- Assert.Equal(wrapingFunctionMapEntry.DeminfifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
- Assert.Null(stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
+ Assert.Equal(wrapingFunctionMapEntry.DeminifiedMethodName, stackFrameDeminification.DeminifiedStackFrame.MethodName);
+ Assert.Equal(SourcePosition.NotFound, stackFrameDeminification.DeminifiedStackFrame.SourcePosition);
Assert.Null(stackFrameDeminification.DeminifiedStackFrame.FilePath);
}
+
+ private static FunctionMapEntry CreateFunctionMapEntry(string deminifiedMethodName)
+ {
+ return new FunctionMapEntry(
+ default!,
+ deminifiedMethodName,
+ default,
+ default);
+ }
+
+ private static SourceMap CreateSourceMap(List? parsedMappings = null)
+ {
+ return new SourceMap(
+ default,
+ default,
+ default,
+ default,
+ default,
+ parsedMappings ?? new List(),
+ default);
+ }
}
}
diff --git a/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierClosureEndToEndTests.cs b/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierClosureEndToEndTests.cs
index 7d349fa..184b201 100644
--- a/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierClosureEndToEndTests.cs
+++ b/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierClosureEndToEndTests.cs
@@ -18,7 +18,7 @@ private static StackTraceDeminifier GetStackTraceDeminifierWithDependencies()
var sourceCodeProvider = new Mock();
sourceCodeProvider.Setup(x => x.GetSourceCode("http://localhost:11323/closurecrashcauser.minified.js")).Returns(UnitTestUtils.StreamFromString(GeneratedCodeString));
- return StackTraceDeminfierFactory.GetStackTraceDeminfier(sourceMapProvider.Object, sourceCodeProvider.Object);
+ return StackTraceDeminifierFactory.GetStackTraceDeminfier(sourceMapProvider.Object, sourceCodeProvider.Object);
}
private static void ValidateDeminifyStackTraceResults(DeminifyStackTraceResult results)
diff --git a/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierEndToEndTests.cs b/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierEndToEndTests.cs
index 05d26aa..b869f21 100644
--- a/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierEndToEndTests.cs
+++ b/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierEndToEndTests.cs
@@ -19,7 +19,7 @@ private static StackTraceDeminifier GetStackTraceDeminifierWithDependencies()
var sourceCodeProvider = new Mock();
sourceCodeProvider.Setup(x => x.GetSourceCode("http://localhost:11323/crashcauser.min.js")).Returns(UnitTestUtils.StreamFromString(GeneratedCodeString));
- return StackTraceDeminfierFactory.GetStackTraceDeminfier(sourceMapProvider.Object, sourceCodeProvider.Object);
+ return StackTraceDeminifierFactory.GetStackTraceDeminfier(sourceMapProvider.Object, sourceCodeProvider.Object);
}
private static void ValidateDeminifyStackTraceResults(DeminifyStackTraceResult results)
diff --git a/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierMapOnlyEndToEndTests.cs b/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierMapOnlyEndToEndTests.cs
index b072474..070c8f8 100644
--- a/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierMapOnlyEndToEndTests.cs
+++ b/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierMapOnlyEndToEndTests.cs
@@ -19,7 +19,7 @@ private static StackTraceDeminifier GetStackTraceDeminifierWithDependencies()
var sourceCodeProvider = new Mock();
sourceCodeProvider.Setup(x => x.GetSourceCode("http://localhost:11323/crashcauser.min.js")).Returns(UnitTestUtils.StreamFromString(GeneratedCodeString));
- return StackTraceDeminfierFactory.GetMapOnlyStackTraceDeminfier(sourceMapProvider.Object);
+ return StackTraceDeminifierFactory.GetMapOnlyStackTraceDeminfier(sourceMapProvider.Object);
}
private static void ValidateDeminifyStackTraceResults(DeminifyStackTraceResult results)
diff --git a/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierWebpackEndToEndTests.cs b/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierWebpackEndToEndTests.cs
index 63adbb4..160a733 100644
--- a/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierWebpackEndToEndTests.cs
+++ b/tests/SourcemapTools.UnitTests/CallstackDeminifier/StackTraceDeminifierWebpackEndToEndTests.cs
@@ -30,7 +30,7 @@ public class StackTraceDeminifierWebpackEndToEndTests
private static StackTraceDeminifier GetStackTraceDeminifierWithDependencies()
{
var provider = new WebpackTestProvider();
- return StackTraceDeminfierFactory.GetStackTraceDeminfier(provider, provider);
+ return StackTraceDeminifierFactory.GetStackTraceDeminfier(provider, provider);
}
[Fact]
diff --git a/tests/SourcemapTools.UnitTests/SourcemapParser/IReadOnlyListExtensionsUnitTests.cs b/tests/SourcemapTools.UnitTests/SourcemapParser/IReadOnlyListExtensionsUnitTests.cs
new file mode 100644
index 0000000..58868fd
--- /dev/null
+++ b/tests/SourcemapTools.UnitTests/SourcemapParser/IReadOnlyListExtensionsUnitTests.cs
@@ -0,0 +1,95 @@
+using System.Collections.Generic;
+using Xunit;
+
+namespace SourcemapToolkit.SourcemapParser.UnitTests
+{
+ public class IReadOnlyListExtensionsUnitTests
+ {
+ [Fact]
+ public void IndexOf_ValueInList_CorrectlyReturnsIndex()
+ {
+ // Arrange
+ var list = new[] { 6, 4, 1, 8 };
+
+ // Act
+ var index = IReadOnlyListExtensions.IndexOf(list, 1);
+
+ // Assert
+ Assert.Equal(2, index);
+ }
+
+ [Fact]
+ public void IndexOf_ValueNotInList_CorrectlyReturnsNegativeOne()
+ {
+ // Arrange
+ var list = new[] { 2, 4, 6, 8 };
+
+ // Act
+ var index = IReadOnlyListExtensions.IndexOf(list, 1);
+
+ // Assert
+ Assert.Equal(-1, index);
+ }
+
+ [Fact]
+ public void IndexOf_ValueAppearsMultipleTimes_CorrectlyReturnsFirstInstance()
+ {
+ // Arrange
+ IReadOnlyList list = new[] { 2, 4, 6, 8, 4 };
+
+ // Act
+ var index = IReadOnlyListExtensions.IndexOf(list, 4);
+
+ // Assert
+ Assert.Equal(1, index);
+ }
+
+ [Fact]
+ public void BinarySearch_EvenNumberOfElements_CorrectlyMatchesListImplementation()
+ {
+ // Arrange
+ // 6 elements total
+ const int minFillIndexInclusive = 1;
+ const int maxFillIndexInclusive = 4;
+
+ var comparer = Comparer.Default;
+ var list = new List();
+
+ for (var i = minFillIndexInclusive; i <= maxFillIndexInclusive; i++)
+ {
+ list.Add(2 * i); // multiplying each entry by 2 to make sure there are gaps
+ }
+
+ // Act & Assert
+ for (var i = minFillIndexInclusive - 1; i <= maxFillIndexInclusive + 1; i++)
+ {
+ Assert.Equal(list.BinarySearch(i, comparer), IReadOnlyListExtensions.BinarySearch(list, i, comparer));
+ Assert.Equal(list.BinarySearch(i + 1, comparer), IReadOnlyListExtensions.BinarySearch(list, i + 1, comparer));
+ }
+ }
+
+ [Fact]
+ public void BinarySearch_OddNumberOfElements_CorrectlyMatchesListImplementation()
+ {
+ // Arrange
+ // 6 elements total
+ const int minFillIndexInclusive = 1;
+ const int maxFillIndexInclusive = 5;
+
+ var comparer = Comparer.Default;
+ var list = new List();
+
+ for (var i = minFillIndexInclusive; i <= maxFillIndexInclusive; i++)
+ {
+ list.Add(2 * i); // multiplying each entry by 2 to make sure there are gaps
+ }
+
+ // Act & Assert
+ for (var i = minFillIndexInclusive - 1; i <= maxFillIndexInclusive + 1; i++)
+ {
+ Assert.Equal(list.BinarySearch(i, comparer), IReadOnlyListExtensions.BinarySearch(list, i, comparer));
+ Assert.Equal(list.BinarySearch(i + 1, comparer), IReadOnlyListExtensions.BinarySearch(list, i + 1, comparer));
+ }
+ }
+ }
+}
diff --git a/tests/SourcemapTools.UnitTests/SourcemapParser/NumericMappingEntryUnitTests.cs b/tests/SourcemapTools.UnitTests/SourcemapParser/NumericMappingEntryUnitTests.cs
index 90042f7..2d590a2 100644
--- a/tests/SourcemapTools.UnitTests/SourcemapParser/NumericMappingEntryUnitTests.cs
+++ b/tests/SourcemapTools.UnitTests/SourcemapParser/NumericMappingEntryUnitTests.cs
@@ -23,7 +23,7 @@ public void ToMappingEntry_ContainsGeneratedSourcePosition_CorrectMappingEntryFi
// Assert
Assert.Equal(12, mappingEntry.GeneratedSourcePosition.Column);
Assert.Equal(13, mappingEntry.GeneratedSourcePosition.Line);
- Assert.Null(mappingEntry.OriginalSourcePosition);
+ Assert.Equal(SourcePosition.NotFound, mappingEntry.OriginalSourcePosition);
Assert.Null(mappingEntry.OriginalFileName);
Assert.Null(mappingEntry.OriginalName);
}
@@ -74,7 +74,7 @@ public void ToMappingEntry_ContainsGeneratedPositionNameIndexAndSourcesIndex_Cor
// Assert
Assert.Equal(8, mappingEntry.GeneratedSourcePosition.Column);
Assert.Equal(48, mappingEntry.GeneratedSourcePosition.Line);
- Assert.Null(mappingEntry.OriginalSourcePosition);
+ Assert.Equal(SourcePosition.NotFound, mappingEntry.OriginalSourcePosition);
Assert.Equal("three", mappingEntry.OriginalFileName);
Assert.Equal("bar", mappingEntry.OriginalName);
}
diff --git a/tests/SourcemapTools.UnitTests/SourcemapParser/SourceMapGeneratorUnitTests.cs b/tests/SourcemapTools.UnitTests/SourcemapParser/SourceMapGeneratorUnitTests.cs
index 6b947d6..5403a62 100644
--- a/tests/SourcemapTools.UnitTests/SourcemapParser/SourceMapGeneratorUnitTests.cs
+++ b/tests/SourcemapTools.UnitTests/SourcemapParser/SourceMapGeneratorUnitTests.cs
@@ -13,7 +13,7 @@ public void SerializeMappingEntry_DifferentLineNumber_SemicolonAdded()
{
// Arrange
var state = new MappingGenerateState(new List() { "Name" }, new List() { "Source" });
- state.LastGeneratedPosition.Column = 1;
+ state.UpdateLastGeneratedPositionColumn(1);
var entry = new MappingEntry(
new SourcePosition(1, 0),
@@ -76,7 +76,7 @@ public void SerializeMappingEntry_WithOriginalFileNameAndOriginalName_FiveSegmen
{
// Arrange
var state = new MappingGenerateState(new List() { "Name" }, new List() { "Source" });
- state.LastGeneratedPosition.Line = 1;
+ state.AdvanceLastGeneratedPositionLine();
var entry = new MappingEntry(
new SourcePosition(1, 5),
@@ -140,33 +140,36 @@ public void SerializeMappingBase64_SimpleSourceMap_CorrectlySerialized()
private static SourceMap GetSimpleSourceMap()
{
- var input = new SourceMap()
- {
- File = "CommonIntl",
- Names = new List() { "CommonStrings", "afrikaans" },
- Sources = new List() { "input/CommonIntl.js" },
- Version = 3,
- };
- input.ParsedMappings.AddRange(new MappingEntry[]
- {
- new MappingEntry(
- new SourcePosition(0, 0),
- new SourcePosition(1, 0),
- input.Names[0],
- input.Sources[0]),
- new MappingEntry(
- new SourcePosition(0, 13),
- new SourcePosition(1, 0),
- null,
- input.Sources[0]),
- new MappingEntry(
- new SourcePosition(0, 14),
- new SourcePosition(1, 14),
- null,
- input.Sources[0])
- });
-
- return input;
+ var sources = new List() { "input/CommonIntl.js" };
+ var names = new List() { "CommonStrings", "afrikaans" };
+
+ var parsedMappings = new List()
+ {
+ new MappingEntry(
+ generatedSourcePosition: new SourcePosition(0, 0),
+ originalSourcePosition: new SourcePosition(1, 0),
+ originalName: names[0],
+ originalFileName: sources[0]),
+ new MappingEntry(
+ generatedSourcePosition: new SourcePosition(0, 13),
+ originalSourcePosition: new SourcePosition(1, 0),
+ null,
+ originalFileName: sources[0]),
+ new MappingEntry(
+ generatedSourcePosition: new SourcePosition(0, 14),
+ originalSourcePosition: new SourcePosition(1, 14),
+ null,
+ originalFileName: sources[0]),
+ };
+
+ return new SourceMap(
+ version: 3,
+ file: "CommonIntl",
+ mappings: default,
+ sources: sources,
+ names: names,
+ parsedMappings: parsedMappings,
+ sourcesContent: default);
}
}
}
diff --git a/tests/SourcemapTools.UnitTests/SourcemapParser/SourceMapTransformerUnitTests.cs b/tests/SourcemapTools.UnitTests/SourcemapParser/SourceMapTransformerUnitTests.cs
index 251b79d..1857fef 100644
--- a/tests/SourcemapTools.UnitTests/SourcemapParser/SourceMapTransformerUnitTests.cs
+++ b/tests/SourcemapTools.UnitTests/SourcemapParser/SourceMapTransformerUnitTests.cs
@@ -52,18 +52,14 @@ public void FlattenMap_MultipleMappingsSameLine_ReturnsOnlyOneMappingPerLine()
var original2 = UnitTestUtils.GenerateSourcePosition(lineNumber: 2, colNumber: 5);
var mappingEntry2 = UnitTestUtils.GetSimpleEntry(generated2, original2, "sourceOne.js");
- var map = new SourceMap
- {
- File = "generated.js",
- Sources = new List { "sourceOne.js" },
- SourcesContent = new List{"var a = b"}
- };
- map.ParsedMappings.AddRange(new MappingEntry[]
- {
- mappingEntry,
- mappingEntry2
- });
-
+ var map = new SourceMap(
+ version: default,
+ file: "generated.js",
+ mappings: default,
+ sources: new List() { "sourceOne.js" },
+ names: default,
+ parsedMappings: new List { mappingEntry, mappingEntry2 },
+ sourcesContent: new List { "var a = b" });
// Act
var linesOnlyMap = SourceMapTransformer.Flatten(map);
@@ -91,13 +87,14 @@ public void FlattenMap_MultipleOriginalLineToSameGeneratedLine_ReturnsFirstOrigi
var original2 = UnitTestUtils.GenerateSourcePosition(lineNumber: 3, colNumber: 5);
var mappingEntry2 = UnitTestUtils.GetSimpleEntry(generated2, original2, "sourceOne.js");
- var map = new SourceMap
- {
- File = "generated.js",
- Sources = new List { "sourceOne.js" },
- SourcesContent = new List{"var a = b"}
- };
- map.ParsedMappings.AddRange(new[] { mappingEntry, mappingEntry2 });
+ var map = new SourceMap(
+ version: default,
+ file: "generated.js",
+ mappings: default,
+ sources: new List() { "sourceOne.js" },
+ names: default,
+ parsedMappings: new List { mappingEntry, mappingEntry2 },
+ sourcesContent: new List { "var a = b" });
// Act
diff --git a/tests/SourcemapTools.UnitTests/SourcemapParser/SourceMapUnitTests.cs b/tests/SourcemapTools.UnitTests/SourcemapParser/SourceMapUnitTests.cs
index 49390a6..0bc070f 100644
--- a/tests/SourcemapTools.UnitTests/SourcemapParser/SourceMapUnitTests.cs
+++ b/tests/SourcemapTools.UnitTests/SourcemapParser/SourceMapUnitTests.cs
@@ -11,8 +11,8 @@ public class SourceMapUnitTests
public void GetMappingEntryForGeneratedSourcePosition_NullMappingList_ReturnNull()
{
// Arrange
- var sourceMap = new SourceMap();
- var sourcePosition = new SourcePosition(3, 4);
+ var sourceMap = CreateSourceMap();
+ var sourcePosition = new SourcePosition(4, 3);
// Act
var result = sourceMap.GetMappingEntryForGeneratedSourcePosition(sourcePosition);
@@ -25,9 +25,14 @@ public void GetMappingEntryForGeneratedSourcePosition_NullMappingList_ReturnNull
public void GetMappingEntryForGeneratedSourcePosition_NoMatchingEntryInMappingList_ReturnNull()
{
// Arrange
- var sourceMap = new SourceMap();
- sourceMap.ParsedMappings.Add(new MappingEntry(new SourcePosition(0, 0)));
- var sourcePosition = new SourcePosition(3, 4);
+ var parsedMappings = new List
+ {
+ new MappingEntry(
+ generatedSourcePosition: new SourcePosition(0, 0))
+ };
+
+ var sourceMap = CreateSourceMap(parsedMappings: parsedMappings);
+ var sourcePosition = new SourcePosition(4, 3);
// Act
var result = sourceMap.GetMappingEntryForGeneratedSourcePosition(sourcePosition);
@@ -40,13 +45,16 @@ public void GetMappingEntryForGeneratedSourcePosition_NoMatchingEntryInMappingLi
public void GetMappingEntryForGeneratedSourcePosition_MatchingEntryInMappingList_ReturnMatchingEntry()
{
// Arrange
- var sourceMap = new SourceMap();
- var matchingMappingEntry = new MappingEntry(new SourcePosition(8, 13));
- sourceMap.ParsedMappings.AddRange(new MappingEntry[]
+ var matchingMappingEntry = new MappingEntry(
+ generatedSourcePosition: new SourcePosition(8, 13));
+ var parsedMappings = new List
{
- new MappingEntry(new SourcePosition(0, 0)),
+ new MappingEntry(
+ generatedSourcePosition: new SourcePosition(0, 0)),
matchingMappingEntry
- });
+ };
+
+ var sourceMap = CreateSourceMap(parsedMappings: parsedMappings);
var sourcePosition = new SourcePosition(8, 13);
// Act
@@ -60,13 +68,15 @@ public void GetMappingEntryForGeneratedSourcePosition_MatchingEntryInMappingList
public void GetMappingEntryForGeneratedSourcePosition_NoExactMatchHasSimilarOnSameLine_ReturnSimilarEntry()
{
// Arrange
- var sourceMap = new SourceMap();
- var matchingMappingEntry = new MappingEntry(new SourcePosition(10, 13));
- sourceMap.ParsedMappings.AddRange(new MappingEntry[]
+ var matchingMappingEntry = new MappingEntry(
+ generatedSourcePosition: new SourcePosition(10, 13));
+ var parsedMappings = new List
{
- new MappingEntry(new SourcePosition(0, 0)),
+ new MappingEntry(
+ generatedSourcePosition: new SourcePosition(0, 0)),
matchingMappingEntry
- });
+ };
+ var sourceMap = CreateSourceMap(parsedMappings: parsedMappings);
var sourcePosition = new SourcePosition(10, 14);
// Act
@@ -80,13 +90,15 @@ public void GetMappingEntryForGeneratedSourcePosition_NoExactMatchHasSimilarOnSa
public void GetMappingEntryForGeneratedSourcePosition_NoExactMatchHasSimilarOnDifferentLinesLine_ReturnSimilarEntry()
{
// Arrange
- var sourceMap = new SourceMap();
- var matchingMappingEntry = new MappingEntry(new SourcePosition(23, 15));
- sourceMap.ParsedMappings.AddRange(new MappingEntry[]
+ var matchingMappingEntry = new MappingEntry(
+ generatedSourcePosition: new SourcePosition(23, 15));
+ var parsedMappings = new List
{
- new MappingEntry(new SourcePosition(0, 0)),
+ new MappingEntry(
+ generatedSourcePosition: new SourcePosition(0, 0)),
matchingMappingEntry
- });
+ };
+ var sourceMap = CreateSourceMap(parsedMappings: parsedMappings);
var sourcePosition = new SourcePosition(24, 0);
// Act
@@ -100,15 +112,13 @@ public void GetMappingEntryForGeneratedSourcePosition_NoExactMatchHasSimilarOnDi
public void GetRootMappingEntryForGeneratedSourcePosition_NoChildren_ReturnsSameEntry()
{
// Arrange
- var generated1 = UnitTestUtils.GenerateSourcePosition(lineNumber:2, colNumber: 5);
- var original1 = UnitTestUtils.GenerateSourcePosition(lineNumber:1, colNumber: 5);
+ var generated1 = UnitTestUtils.GenerateSourcePosition(lineNumber: 2, colNumber: 5);
+ var original1 = UnitTestUtils.GenerateSourcePosition(lineNumber: 1, colNumber: 5);
var mappingEntry = UnitTestUtils.GetSimpleEntry(generated1, original1, "generated.js");
- var sourceMap = new SourceMap
- {
- Sources = new List { "generated.js" },
- };
- sourceMap.ParsedMappings.Add(mappingEntry);
+ var sourceMap = CreateSourceMap(
+ sources: new List() { "generated.js" },
+ parsedMappings: new List { mappingEntry });
// Act
var rootEntry = sourceMap.GetMappingEntryForGeneratedSourcePosition(generated1);
@@ -121,16 +131,15 @@ public void GetRootMappingEntryForGeneratedSourcePosition_NoChildren_ReturnsSame
public void ApplyMap_NullSubmap_ThrowsException()
{
// Arrange
- var generated2 = UnitTestUtils.GenerateSourcePosition(lineNumber:3, colNumber: 5);
- var original2 = UnitTestUtils.GenerateSourcePosition(lineNumber:2, colNumber: 5);
+ var generated2 = UnitTestUtils.GenerateSourcePosition(lineNumber: 3, colNumber: 5);
+ var original2 = UnitTestUtils.GenerateSourcePosition(lineNumber: 2, colNumber: 5);
var mapping = UnitTestUtils.GetSimpleEntry(generated2, original2, "sourceOne.js");
- var map = new SourceMap
- {
- File = "generated.js",
- Sources = new List { "sourceOne.js" }
- };
- map.ParsedMappings.Add(mapping);
+ var map = CreateSourceMap(
+ file: "generated.js",
+ sources: new List() { "sourceOne.js" },
+ parsedMappings: new List { mapping });
+
// Act
Assert.Throws(() => map.ApplySourceMap(null!));
@@ -142,27 +151,23 @@ public void ApplyMap_NullSubmap_ThrowsException()
public void ApplyMap_NoMatchingSources_ReturnsSameMap()
{
// Arrange
- var generated1 = UnitTestUtils.GenerateSourcePosition(lineNumber:2, colNumber: 3);
- var original1 = UnitTestUtils.GenerateSourcePosition(lineNumber:1, colNumber: 2);
+ var generated1 = UnitTestUtils.GenerateSourcePosition(lineNumber: 2, colNumber: 3);
+ var original1 = UnitTestUtils.GenerateSourcePosition(lineNumber: 1, colNumber: 2);
var childMapping = UnitTestUtils.GetSimpleEntry(generated1, original1, "someOtherSource.js");
- var childMap = new SourceMap
- {
- File = "notSourceOne.js",
- Sources = new List { "someOtherSource.js" }
- };
- childMap.ParsedMappings.Add(childMapping);
+ var childMap = CreateSourceMap(
+ file: "notSourceOne.js",
+ sources: new List() { "someOtherSource.js" },
+ parsedMappings: new List { childMapping });
- var generated2 = UnitTestUtils.GenerateSourcePosition(lineNumber:3, colNumber: 7);
- var original2 = UnitTestUtils.GenerateSourcePosition(lineNumber:2, colNumber: 3);
+ var generated2 = UnitTestUtils.GenerateSourcePosition(lineNumber: 3, colNumber: 7);
+ var original2 = UnitTestUtils.GenerateSourcePosition(lineNumber: 2, colNumber: 3);
var parentMapping = UnitTestUtils.GetSimpleEntry(generated2, original2, "sourceOne.js");
- var parentMap = new SourceMap
- {
- File = "generated.js",
- Sources = new List { "sourceOne.js" }
- };
- parentMap.ParsedMappings.Add(parentMapping);
+ var parentMap = CreateSourceMap(
+ file: "generated.js",
+ sources: new List() { "sourceOne.js" },
+ parsedMappings: new List { parentMapping });
// Act
var combinedMap = parentMap.ApplySourceMap(childMap);
@@ -177,27 +182,23 @@ public void ApplyMap_NoMatchingSources_ReturnsSameMap()
public void ApplyMap_NoMatchingMappings_ReturnsSameMap()
{
// Arrange
- var generated1 = UnitTestUtils.GenerateSourcePosition(lineNumber: 2, colNumber:2);
- var original1 = UnitTestUtils.GenerateSourcePosition(lineNumber: 1, colNumber:10);
+ var generated1 = UnitTestUtils.GenerateSourcePosition(lineNumber: 2, colNumber: 2);
+ var original1 = UnitTestUtils.GenerateSourcePosition(lineNumber: 1, colNumber: 10);
var childMapping = UnitTestUtils.GetSimpleEntry(generated1, original1, "sourceTwo.js");
- var childMap = new SourceMap
- {
- File = "sourceOne.js",
- Sources = new List { "sourceTwo.js" }
- };
- childMap.ParsedMappings.Add(childMapping);
+ var childMap = CreateSourceMap(
+ file: "sourceOne.js",
+ sources: new List() { "sourceTwo.js" },
+ parsedMappings: new List { childMapping });
- var generated2 = UnitTestUtils.GenerateSourcePosition(lineNumber:3, colNumber:4);
- var original2 = UnitTestUtils.GenerateSourcePosition(lineNumber:2, colNumber:5);
+ var generated2 = UnitTestUtils.GenerateSourcePosition(lineNumber: 3, colNumber: 4);
+ var original2 = UnitTestUtils.GenerateSourcePosition(lineNumber: 2, colNumber: 5);
var parentMapping = UnitTestUtils.GetSimpleEntry(generated2, original2, "sourceOne.js");
- var parentMap = new SourceMap
- {
- File = "generated.js",
- Sources = new List { "sourceOne.js" }
- };
- parentMap.ParsedMappings.Add(parentMapping);
+ var parentMap = CreateSourceMap(
+ file: "generated.js",
+ sources: new List() { "sourceOne.js" },
+ parsedMappings: new List { parentMapping });
// Act
var combinedMap = parentMap.ApplySourceMap(childMap);
@@ -214,27 +215,23 @@ public void ApplyMap_MatchingSources_ReturnsCorrectMap()
// Expect mapping with same source filename as the applied source-map to be replaced
// Arrange
- var generated1 = UnitTestUtils.GenerateSourcePosition(lineNumber:2, colNumber:4);
- var original1 = UnitTestUtils.GenerateSourcePosition(lineNumber:1, colNumber:3);
+ var generated1 = UnitTestUtils.GenerateSourcePosition(lineNumber: 2, colNumber: 4);
+ var original1 = UnitTestUtils.GenerateSourcePosition(lineNumber: 1, colNumber: 3);
var childMapping = UnitTestUtils.GetSimpleEntry(generated1, original1, "sourceTwo.js");
- var childMap = new SourceMap
- {
- File = "sourceOne.js",
- Sources = new List { "sourceTwo.js" }
- };
- childMap.ParsedMappings.Add(childMapping);
+ var childMap = CreateSourceMap(
+ file: "sourceOne.js",
+ sources: new List() { "sourceTwo.js" },
+ parsedMappings: new List { childMapping });
- var generated2 = UnitTestUtils.GenerateSourcePosition(lineNumber:3, colNumber: 5);
- var original2 = UnitTestUtils.GenerateSourcePosition(lineNumber:2, colNumber: 4);
+ var generated2 = UnitTestUtils.GenerateSourcePosition(lineNumber: 3, colNumber: 5);
+ var original2 = UnitTestUtils.GenerateSourcePosition(lineNumber: 2, colNumber: 4);
var parentMapping = UnitTestUtils.GetSimpleEntry(generated2, original2, "sourceOne.js");
- var parentMap = new SourceMap
- {
- File = "generated.js",
- Sources = new List { "sourceOne.js" }
- };
- parentMap.ParsedMappings.Add(parentMapping);
+ var parentMap = CreateSourceMap(
+ file: "generated.js",
+ sources: new List() { "sourceOne.js" },
+ parsedMappings: new List { parentMapping });
// Act
var combinedMap = parentMap.ApplySourceMap(childMap)!;
@@ -244,7 +241,7 @@ public void ApplyMap_MatchingSources_ReturnsCorrectMap()
Assert.Single(combinedMap.ParsedMappings);
Assert.Single(combinedMap.Sources);
var rootMapping = combinedMap.GetMappingEntryForGeneratedSourcePosition(generated2)!;
- Assert.Equal(0, rootMapping.OriginalSourcePosition!.CompareTo(childMapping.OriginalSourcePosition!));
+ Assert.Equal(0, rootMapping.Value.OriginalSourcePosition!.CompareTo(childMapping.OriginalSourcePosition!));
}
[Fact]
@@ -254,31 +251,27 @@ public void ApplyMap_PartialMatchingSources_ReturnsCorrectMap()
// mappings with a different source filename should stay the same
// Arrange
- var generated1 = UnitTestUtils.GenerateSourcePosition(lineNumber:2, colNumber:10);
- var original1 = UnitTestUtils.GenerateSourcePosition(lineNumber:1, colNumber:5);
+ var generated1 = UnitTestUtils.GenerateSourcePosition(lineNumber: 2, colNumber: 10);
+ var original1 = UnitTestUtils.GenerateSourcePosition(lineNumber: 1, colNumber: 5);
var childMapping = UnitTestUtils.GetSimpleEntry(generated1, original1, "sourceTwo.js");
- var childMap = new SourceMap
- {
- File = "sourceOne.js",
- Sources = new List { "sourceTwo.js" }
- };
- childMap.ParsedMappings.Add(childMapping);
+ var childMap = CreateSourceMap(
+ file: "sourceOne.js",
+ sources: new List() { "sourceTwo.js" },
+ parsedMappings: new List { childMapping });
- var generated2 = UnitTestUtils.GenerateSourcePosition(lineNumber:3, colNumber:2);
- var original2 = UnitTestUtils.GenerateSourcePosition(lineNumber:2, colNumber: 10);
+ var generated2 = UnitTestUtils.GenerateSourcePosition(lineNumber: 3, colNumber: 2);
+ var original2 = UnitTestUtils.GenerateSourcePosition(lineNumber: 2, colNumber: 10);
var mapping = UnitTestUtils.GetSimpleEntry(generated2, original2, "sourceOne.js");
- var generated3 = UnitTestUtils.GenerateSourcePosition(lineNumber:4, colNumber:3);
- var original3 = UnitTestUtils.GenerateSourcePosition(lineNumber:3, colNumber:2);
+ var generated3 = UnitTestUtils.GenerateSourcePosition(lineNumber: 4, colNumber: 3);
+ var original3 = UnitTestUtils.GenerateSourcePosition(lineNumber: 3, colNumber: 2);
var mapping2 = UnitTestUtils.GetSimpleEntry(generated3, original3, "noMapForThis.js");
- var parentMap = new SourceMap
- {
- File = "generated.js",
- Sources = new List { "sourceOne.js", "noMapForThis.js" }
- };
- parentMap.ParsedMappings.AddRange(new[] { mapping, mapping2 });
+ var parentMap = CreateSourceMap(
+ file: "generated.js",
+ sources: new List { "sourceOne.js", "noMapForThis.js" },
+ parsedMappings: new List { mapping, mapping2 });
// Act
var combinedMap = parentMap.ApplySourceMap(childMap);
@@ -288,47 +281,41 @@ public void ApplyMap_PartialMatchingSources_ReturnsCorrectMap()
Assert.Equal(2, combinedMap.ParsedMappings.Count);
Assert.Equal(2, combinedMap.Sources!.Count);
var firstCombinedMapping = combinedMap.GetMappingEntryForGeneratedSourcePosition(generated3)!;
- Assert.True(firstCombinedMapping.IsValueEqual(mapping2));
+ Assert.True(firstCombinedMapping.Value.IsValueEqual(mapping2));
var secondCombinedMapping = combinedMap.GetMappingEntryForGeneratedSourcePosition(generated2)!;
- Assert.Equal(0, secondCombinedMapping.OriginalSourcePosition!.CompareTo(childMapping.OriginalSourcePosition!));
+ Assert.Equal(0, secondCombinedMapping.Value.OriginalSourcePosition!.CompareTo(childMapping.OriginalSourcePosition!));
}
[Fact]
public void ApplyMap_ExactMatchDeep_ReturnsCorrectMappingEntry()
{
// Arrange
- var generated1 = UnitTestUtils.GenerateSourcePosition(lineNumber:3, colNumber:5);
- var original1 = UnitTestUtils.GenerateSourcePosition(lineNumber:2, colNumber:10);
+ var generated1 = UnitTestUtils.GenerateSourcePosition(lineNumber: 3, colNumber: 5);
+ var original1 = UnitTestUtils.GenerateSourcePosition(lineNumber: 2, colNumber: 10);
var mapLevel2 = UnitTestUtils.GetSimpleEntry(generated1, original1, "sourceThree.js");
- var grandChildMap = new SourceMap
- {
- File = "sourceTwo.js",
- Sources = new List { "sourceThree.js" }
- };
- grandChildMap.ParsedMappings.Add(mapLevel2);
+ var grandChildMap = CreateSourceMap(
+ file: "sourceTwo.js",
+ sources: new List() { "sourceThree.js" },
+ parsedMappings: new List { mapLevel2 });
- var generated2 = UnitTestUtils.GenerateSourcePosition(lineNumber:4, colNumber:3);
- var original2 = UnitTestUtils.GenerateSourcePosition(lineNumber:3, colNumber:5);
+ var generated2 = UnitTestUtils.GenerateSourcePosition(lineNumber: 4, colNumber: 3);
+ var original2 = UnitTestUtils.GenerateSourcePosition(lineNumber: 3, colNumber: 5);
var mapLevel1 = UnitTestUtils.GetSimpleEntry(generated2, original2, "sourceTwo.js");
- var childMap = new SourceMap
- {
- File = "sourceOne.js",
- Sources = new List { "sourceTwo.js" }
- };
- childMap.ParsedMappings.Add(mapLevel1);
+ var childMap = CreateSourceMap(
+ file: "sourceOne.js",
+ sources: new List() { "sourceTwo.js" },
+ parsedMappings: new List { mapLevel1 });
- var generated3 = UnitTestUtils.GenerateSourcePosition(lineNumber:5, colNumber:5);
- var original3 = UnitTestUtils.GenerateSourcePosition(lineNumber:4, colNumber:3);
+ var generated3 = UnitTestUtils.GenerateSourcePosition(lineNumber: 5, colNumber: 5);
+ var original3 = UnitTestUtils.GenerateSourcePosition(lineNumber: 4, colNumber: 3);
var mapLevel0 = UnitTestUtils.GetSimpleEntry(generated3, original3, "sourceOne.js");
- var parentMap = new SourceMap
- {
- File = "generated.js",
- Sources = new List { "sourceOne.js" }
- };
- parentMap.ParsedMappings.Add(mapLevel0);
+ var parentMap = CreateSourceMap(
+ file: "generated.js",
+ sources: new List() { "sourceOne.js" },
+ parsedMappings: new List { mapLevel0 });
// Act
var firstCombinedMap = parentMap.ApplySourceMap(childMap);
@@ -338,7 +325,26 @@ public void ApplyMap_ExactMatchDeep_ReturnsCorrectMappingEntry()
var secondCombinedMap = firstCombinedMap.ApplySourceMap(grandChildMap);
Assert.NotNull(secondCombinedMap);
var rootMapping = secondCombinedMap.GetMappingEntryForGeneratedSourcePosition(generated3)!;
- Assert.Equal(0, rootMapping.OriginalSourcePosition!.CompareTo(mapLevel2.OriginalSourcePosition!));
+ Assert.Equal(0, rootMapping.Value.OriginalSourcePosition!.CompareTo(mapLevel2.OriginalSourcePosition!));
+ }
+
+ private static SourceMap CreateSourceMap(
+ int version = default,
+ string? file = default,
+ string? mappings = default,
+ IReadOnlyList? sources = default,
+ IReadOnlyList? names = default,
+ IReadOnlyList? parsedMappings = default,
+ IReadOnlyList? sourcesContent = default)
+ {
+ return new SourceMap(
+ version: version,
+ file: file,
+ mappings: mappings,
+ sources: sources,
+ names: names,
+ parsedMappings: parsedMappings ?? new List(),
+ sourcesContent: sourcesContent);
}
}
}
diff --git a/tests/SourcemapTools.UnitTests/SourcemapParser/StringExtensionsUnitTests.cs b/tests/SourcemapTools.UnitTests/SourcemapParser/StringExtensionsUnitTests.cs
new file mode 100644
index 0000000..c7012eb
--- /dev/null
+++ b/tests/SourcemapTools.UnitTests/SourcemapParser/StringExtensionsUnitTests.cs
@@ -0,0 +1,185 @@
+using System;
+using Xunit;
+
+namespace SourcemapToolkit.SourcemapParser.UnitTests
+{
+ public class StringExtensionsUnitTests
+ {
+ [Fact]
+ public void SplitFast_NullInput_Throws()
+ {
+ // Arrange
+ const string? input = null;
+ const char delimiter = ',';
+
+ // Act
+ static void testAction()
+ {
+ StringExtensions.SplitFast(input!, delimiter);
+ }
+
+ //Throws Assert
+ Assert.Throws(testAction);
+ }
+
+ [Fact]
+ public void SplitFast_EmptyInput_Matches()
+ {
+ // Arrange
+ const string input = "";
+ const char delimiter = ',';
+
+ // Act
+ var fastSplit = StringExtensions.SplitFast(input, delimiter);
+ var normalSplit = input.Split(delimiter);
+
+ // Assert
+ Assert.Equal(normalSplit.Length, fastSplit.Length);
+
+ for (var i = 0; i < normalSplit.Length; i++)
+ {
+ Assert.Equal(normalSplit[i], fastSplit[i]);
+ }
+ }
+
+ [Fact]
+ public void SplitFast_OneCharacterNotSplit_Matches()
+ {
+ // Arrange
+ const string input = "A";
+ const char delimiter = ',';
+
+ // Act
+ var fastSplit = StringExtensions.SplitFast(input, delimiter);
+ var normalSplit = input.Split(delimiter);
+
+ // Assert
+ Assert.Equal(normalSplit.Length, fastSplit.Length);
+
+ for (var i = 0; i < normalSplit.Length; i++)
+ {
+ Assert.Equal(normalSplit[i], fastSplit[i]);
+ }
+ }
+
+ [Fact]
+ public void SplitFast_OneCharacterDelimiter_Matches()
+ {
+ // Arrange
+ const string input = ",";
+ const char delimiter = ',';
+
+ // Act
+ var fastSplit = StringExtensions.SplitFast(input, delimiter);
+ var normalSplit = input.Split(delimiter);
+
+ // Assert
+ Assert.Equal(normalSplit.Length, fastSplit.Length);
+
+ for (var i = 0; i < normalSplit.Length; i++)
+ {
+ Assert.Equal(normalSplit[i], fastSplit[i]);
+ }
+ }
+
+ [Fact]
+ public void SplitFast_DelimiterAtStart_Matches()
+ {
+ // Arrange
+ const string input = ",Hello";
+ const char delimiter = ',';
+
+ // Act
+ var fastSplit = StringExtensions.SplitFast(input, delimiter);
+ var normalSplit = input.Split(delimiter);
+
+ // Assert
+ Assert.Equal(normalSplit.Length, fastSplit.Length);
+
+ for (var i = 0; i < normalSplit.Length; i++)
+ {
+ Assert.Equal(normalSplit[i], fastSplit[i]);
+ }
+ }
+
+ [Fact]
+ public void SplitFast_DelimiterAtEnd_Matches()
+ {
+ // Arrange
+ const string input = "Hello,";
+ const char delimiter = ',';
+
+ // Act
+ var fastSplit = StringExtensions.SplitFast(input, delimiter);
+ var normalSplit = input.Split(delimiter);
+
+ // Assert
+ Assert.Equal(normalSplit.Length, fastSplit.Length);
+
+ for (var i = 0; i < normalSplit.Length; i++)
+ {
+ Assert.Equal(normalSplit[i], fastSplit[i]);
+ }
+ }
+
+ [Fact]
+ public void SplitFast_BackToBack_Matches()
+ {
+ // Arrange
+ const string input = "Hello,,World";
+ const char delimiter = ',';
+
+ // Act
+ var fastSplit = StringExtensions.SplitFast(input, delimiter);
+ var normalSplit = input.Split(delimiter);
+
+ // Assert
+ Assert.Equal(normalSplit.Length, fastSplit.Length);
+
+ for (var i = 0; i < normalSplit.Length; i++)
+ {
+ Assert.Equal(normalSplit[i], fastSplit[i]);
+ }
+ }
+
+ [Fact]
+ public void SplitFast_LongSentence_Matches()
+ {
+ // Arrange
+ const string input = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna. Nunc viverra imperdiet enim. Fusce est. Vivamus a tellus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin pharetra nonummy pede. Mauris et orci. Aenean nec lorem. In porttitor. Donec laoreet nonummy augue. Suspendisse dui purus, scelerisque at, vulputate vitae, pretium mattis, nunc. Mauris eget neque at sem venenatis eleifend. Ut nonummy.";
+ const char delimiter = ',';
+
+ // Act
+ var fastSplit = StringExtensions.SplitFast(input, delimiter);
+ var normalSplit = input.Split(delimiter);
+
+ // Assert
+ Assert.Equal(normalSplit.Length, fastSplit.Length);
+
+ for (var i = 0; i < normalSplit.Length; i++)
+ {
+ Assert.Equal(normalSplit[i], fastSplit[i]);
+ }
+ }
+
+ [Fact]
+ public void SplitFast_Complex_Matches()
+ {
+ // Arrange
+ const string input = ",,Hello,World,How,,Are,You,Doing,,";
+ const char delimiter = ',';
+
+ // Act
+ var fastSplit = StringExtensions.SplitFast(input, delimiter);
+ var normalSplit = input.Split(delimiter);
+
+ // Assert
+ Assert.Equal(normalSplit.Length, fastSplit.Length);
+
+ for (var i = 0; i < normalSplit.Length; i++)
+ {
+ Assert.Equal(normalSplit[i], fastSplit[i]);
+ }
+ }
+ }
+}