Fix fuzzy logic for comparing source positions when column is zero (#30)

* Update the API for the source map toolkit to take a StreamReader instead of a string.

* Update the comments for the API interfaces to reflect the new return type.

* Call close on streams when done using them.
Update comments.

* Update documentation (#28)

* Update the API for the source map toolkit to take a StreamReader instead of a string.

* Update the comments for the API interfaces to reflect the new return type.

* Call close on streams when done using them.
Update comments.

* Add some comments to the readme file to describe the APIs of the library

* Update main blurb.

* Update description of API usage.

* Repleace Stream with StremReader to correctly reflect the API

* Replace "input string" with "input callstack string"

* Add MIT License information to repository (#29)

* Update the API for the source map toolkit to take a StreamReader instead of a string.

* Update the comments for the API interfaces to reflect the new return type.

* Call close on streams when done using them.
Update comments.

* Update documentation (#28)

* Update the API for the source map toolkit to take a StreamReader instead of a string.

* Update the comments for the API interfaces to reflect the new return type.

* Call close on streams when done using them.
Update comments.

* Add some comments to the readme file to describe the APIs of the library

* Update main blurb.

* Update description of API usage.

* Repleace Stream with StremReader to correctly reflect the API

* Replace "input string" with "input callstack string"

* Add MIT license file to root directory

* Add license section to readme

* Add line breaks to license

* Change casing on License.txt

* Change casing on License.txt

* The fuzzy search logic for matching source positions from wrapping functions to source map positions considers source positions whose column values differ by one to be equivalent. There is currently a bug when the column number of the generated source position is zero, since it will try to find a source position with a column number of -1. This commit will fix this to search for the largest column number on the previous line when this scenario occurs. Note that this is an optimistic guess at the line number, since we don't actually know how many column numbers are on a given line. If we find that this assumption does not hold in practice, we'll have to modify the library to keep track of how many columns are in each line of source code.

* Change ~index  - 1 > 0 to ~index-1 >=0 to allow for the case where ~index is equal to 1.

Add unit test to cover the cross line number case to GetMappingEntryForGeneratedSourcePosition.
This commit is contained in:
Christian Gonzalez 2016-11-14 18:04:34 -08:00 коммит произвёл GitHub
Родитель 17f376ec76 28ac8dfc08
Коммит 5e6025f2cb
4 изменённых файлов: 227 добавлений и 29 удалений

Просмотреть файл

@ -47,35 +47,26 @@ namespace SourcemapToolkit.SourcemapParser
return null;
}
MappingEntry result = MappingEntryForGeneratedSourcePositionExact(generatedSourcePosition);
MappingEntry mappingEntryToFind = new MappingEntry
{
GeneratedSourcePosition = generatedSourcePosition
};
// Attempt to find a nearby result if there is no exact match.
if (result == null)
{
// The mappings from Google Closure Advanced mode often have column numbers that are off by 1
result =
MappingEntryForGeneratedSourcePositionExact(new SourcePosition
{
ZeroBasedColumnNumber = generatedSourcePosition.ZeroBasedColumnNumber - 1,
ZeroBasedLineNumber = generatedSourcePosition.ZeroBasedLineNumber
});
int index = ParsedMappings.BinarySearch(mappingEntryToFind,
Comparer<MappingEntry>.Create((a, b) => a.GeneratedSourcePosition.CompareTo(b.GeneratedSourcePosition)));
}
// 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))
{
index = ~index - 1;
}
}
return result;
}
private MappingEntry MappingEntryForGeneratedSourcePositionExact(SourcePosition generatedSourcePosition)
{
MappingEntry mappingEntryToFind = new MappingEntry
{
GeneratedSourcePosition = generatedSourcePosition
};
int index = ParsedMappings.BinarySearch(mappingEntryToFind,
Comparer<MappingEntry>.Create((a, b) => a.GeneratedSourcePosition.CompareTo(b.GeneratedSourcePosition)));
return index >= 0 ? ParsedMappings[index] : null;
return index >= 0 ? ParsedMappings[index] : null;
}
}
}

Просмотреть файл

@ -30,5 +30,32 @@ namespace SourcemapToolkit.SourcemapParser
{
return x.CompareTo(y) > 0;
}
/// <summary>
/// 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.
/// </summary>
public bool IsEqualish(SourcePosition other)
{
// If the column numbers differ by 1, we can say that the source code is approximately equal
if (this.ZeroBasedLineNumber == other.ZeroBasedLineNumber && Math.Abs(this.ZeroBasedColumnNumber - other.ZeroBasedColumnNumber) <= 1)
{
return true;
}
// This handles the case where we are comparing code at the end of one line and the beginning of the next line.
// If the column number on one of the entries is zero, it is ok for the line numbers to differ by 1, so long as the one that has a column number of zero is the one with the larger line number.
// Since we don't have the number of columns in each line, we can't know for sure if these two pieces of code are actually near each other. This is an optimistic guess.
if (Math.Abs(this.ZeroBasedLineNumber - other.ZeroBasedLineNumber) == 1)
{
SourcePosition largerLineNumber = this.ZeroBasedLineNumber > other.ZeroBasedLineNumber ? this : other;
if (largerLineNumber.ZeroBasedColumnNumber == 0)
{
return true;
}
}
return false;
}
}
}

Просмотреть файл

@ -68,7 +68,7 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
}
[TestMethod]
public void GetMappingEntryForGeneratedSourcePosition_NoExactMatchHasSimilar_ReturnSimilarEntry()
public void GetMappingEntryForGeneratedSourcePosition_NoExactMatchHasSimilarOnSameLine_ReturnSimilarEntry()
{
// Arrange
SourceMap sourceMap = new SourceMap();
@ -92,5 +92,31 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
// Asset
Assert.AreEqual(matchingMappingEntry, result);
}
}
[TestMethod]
public void GetMappingEntryForGeneratedSourcePosition_NoExactMatchHasSimilarOnDifferentLinesLine_ReturnSimilarEntry()
{
// Arrange
SourceMap sourceMap = new SourceMap();
MappingEntry matchingMappingEntry = new MappingEntry
{
GeneratedSourcePosition = new SourcePosition { ZeroBasedLineNumber = 23, ZeroBasedColumnNumber = 15 }
};
sourceMap.ParsedMappings = new List<MappingEntry>
{
new MappingEntry
{
GeneratedSourcePosition = new SourcePosition {ZeroBasedLineNumber = 0, ZeroBasedColumnNumber = 0}
},
matchingMappingEntry
};
SourcePosition sourcePosition = new SourcePosition { ZeroBasedLineNumber = 24, ZeroBasedColumnNumber = 0 };
// Act
MappingEntry result = sourceMap.GetMappingEntryForGeneratedSourcePosition(sourcePosition);
// Asset
Assert.AreEqual(matchingMappingEntry, result);
}
}
}

Просмотреть файл

@ -290,5 +290,159 @@ namespace SourcemapToolkit.SourcemapParser.UnitTests
// Assert
Assert.IsFalse(result);
}
}
[TestMethod]
public void IsEqualish_XEqualsY_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 13,
ZeroBasedColumnNumber = 5
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 13,
ZeroBasedColumnNumber = 5
};
// Act
bool result = x.IsEqualish(y);
// Assert
Assert.IsTrue(result);
}
[TestMethod]
public void IsEqualish_XColumnNumberBiggerByOne_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 2,
ZeroBasedColumnNumber = 8
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 2,
ZeroBasedColumnNumber = 7
};
// Act
bool result = x.IsEqualish(y);
// Assert
Assert.IsTrue(result);
}
[TestMethod]
public void IsEqualish_YColumnNumberBiggerByOne_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 10
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 1,
ZeroBasedColumnNumber = 11
};
// Act
bool result = x.IsEqualish(y);
// Assert
Assert.IsTrue(result);
}
[TestMethod]
public void IsEqualish_YColumnNumberBiggerByTwo_ReturnFalse()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 155,
ZeroBasedColumnNumber = 100
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 155,
ZeroBasedColumnNumber = 102
};
// Act
bool result = x.IsEqualish(y);
// Assert
Assert.IsFalse(result);
}
[TestMethod]
public void IsEqualish_XColumnNumberZeroLineNumbersDifferByOne_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 235,
ZeroBasedColumnNumber = 0
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 234,
ZeroBasedColumnNumber = 102
};
// Act
bool result = x.IsEqualish(y);
// Assert
Assert.IsTrue(result);
}
[TestMethod]
public void IsEqualish_YColumnNumberZeroLineNumbersDifferByOne_ReturnTrue()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 458,
ZeroBasedColumnNumber = 13
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 459,
ZeroBasedColumnNumber = 0
};
// Act
bool result = x.IsEqualish(y);
// Assert
Assert.IsTrue(result);
}
[TestMethod]
public void IsEqualish_YColumnNumberZeroLineNumbersDifferByTwo_ReturnFalse()
{
// Arrange
SourcePosition x = new SourcePosition
{
ZeroBasedLineNumber = 5456,
ZeroBasedColumnNumber = 13
};
SourcePosition y = new SourcePosition
{
ZeroBasedLineNumber = 5458,
ZeroBasedColumnNumber = 0
};
// Act
bool result = x.IsEqualish(y);
// Assert
Assert.IsFalse(result);
}
}
}