Better method name parsing and mapping (#76)
* Fix method name association, add Webpack + TypeScript example test
This commit is contained in:
Родитель
0394d00ff7
Коммит
3fb57e94f5
|
@ -266,4 +266,6 @@ __pycache__/
|
|||
|
||||
# Minified JavaScript
|
||||
*.min.js
|
||||
*.min.js.map
|
||||
*.min.js.map
|
||||
|
||||
!**/webpackapp/**
|
|
@ -49,7 +49,7 @@ namespace SourcemapToolkit.CallstackDeminifier
|
|||
BinaryOperator parentBinaryOperator = node.Parent as BinaryOperator;
|
||||
if (parentBinaryOperator != null)
|
||||
{
|
||||
result.Add(ExtractBindingsFromBinaryOperator(parentBinaryOperator));
|
||||
result.AddRange(ExtractBindingsFromBinaryOperator(parentBinaryOperator));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ namespace SourcemapToolkit.CallstackDeminifier
|
|||
ObjectLiteral objectLiteralParent = parentObjectLiteralProperty.Parent?.Parent as ObjectLiteral;
|
||||
if (objectLiteralParent != null && objectLiteralParent.Parent is BinaryOperator)
|
||||
{
|
||||
result.Add(ExtractBindingsFromBinaryOperator((BinaryOperator)objectLiteralParent.Parent));
|
||||
result.AddRange(ExtractBindingsFromBinaryOperator((BinaryOperator)objectLiteralParent.Parent));
|
||||
}
|
||||
|
||||
result.Add(
|
||||
|
@ -110,15 +110,47 @@ namespace SourcemapToolkit.CallstackDeminifier
|
|||
return null;
|
||||
}
|
||||
|
||||
private BindingInformation ExtractBindingsFromBinaryOperator(BinaryOperator parentBinaryOperator)
|
||||
private IEnumerable<BindingInformation> ExtractBindingsFromBinaryOperator(BinaryOperator parentBinaryOperator)
|
||||
{
|
||||
// If the operand has a dot in the name it's a Member. e.g. a.b, a.prototype, a.prototype.b
|
||||
Member member = parentBinaryOperator.Operand1 as Member;
|
||||
if (member != null)
|
||||
{
|
||||
// Split members into two parts, on the last dot, so a.prototype.b becomes [a.prototype, b]
|
||||
// This separates the generated location for the property/method name (b), and allows deminification
|
||||
// to resolve both the class and method names.
|
||||
yield return ExtractBindingsFromNode(member.Root);
|
||||
|
||||
// a.prototype splits into [a, prototype], but we throw away the prototype as this doesn't map to anything useful in the original source
|
||||
if (member.Name != "prototype")
|
||||
{
|
||||
int offset = member.NameContext.Code.StartsWith(".") ? 1 : 0;
|
||||
yield return new BindingInformation
|
||||
{
|
||||
Name = member.Name,
|
||||
SourcePosition = new SourcePosition
|
||||
{
|
||||
ZeroBasedLineNumber = member.NameContext.StartLineNumber - 1,
|
||||
ZeroBasedColumnNumber = member.NameContext.StartColumn + offset
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return ExtractBindingsFromNode(parentBinaryOperator.Operand1);
|
||||
}
|
||||
}
|
||||
|
||||
private BindingInformation ExtractBindingsFromNode(AstNode node)
|
||||
{
|
||||
return new BindingInformation
|
||||
{
|
||||
Name = parentBinaryOperator.Operand1.Context.Code,
|
||||
Name = node.Context.Code,
|
||||
SourcePosition = new SourcePosition
|
||||
{
|
||||
ZeroBasedLineNumber = parentBinaryOperator.Operand1.Context.StartLineNumber - 1,
|
||||
ZeroBasedColumnNumber = parentBinaryOperator.Operand1.Context.StartColumn
|
||||
ZeroBasedLineNumber = node.Context.StartLineNumber - 1,
|
||||
ZeroBasedColumnNumber = node.Context.StartColumn
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -78,12 +78,11 @@ namespace SourcemapToolkit.CallstackDeminifier
|
|||
|
||||
if (wrappingFunction.Bindings != null && wrappingFunction.Bindings.Count > 0)
|
||||
{
|
||||
MappingEntry objectProtoypeMappingEntry = null;
|
||||
if (wrappingFunction.Bindings.Count == 2)
|
||||
{
|
||||
MappingEntry objectProtoypeMappingEntry =
|
||||
objectProtoypeMappingEntry =
|
||||
sourceMap.GetMappingEntryForGeneratedSourcePosition(wrappingFunction.Bindings[0].SourcePosition);
|
||||
|
||||
methodName = objectProtoypeMappingEntry?.OriginalName;
|
||||
}
|
||||
|
||||
MappingEntry mappingEntry =
|
||||
|
@ -91,9 +90,20 @@ namespace SourcemapToolkit.CallstackDeminifier
|
|||
|
||||
if (mappingEntry?.OriginalName != null)
|
||||
{
|
||||
if (methodName != null)
|
||||
if (objectProtoypeMappingEntry?.OriginalName != null)
|
||||
{
|
||||
methodName = methodName + "." + mappingEntry.OriginalName;
|
||||
string objectName = objectProtoypeMappingEntry.OriginalName;
|
||||
if (objectProtoypeMappingEntry.OriginalSourcePosition?.ZeroBasedColumnNumber == mappingEntry.OriginalSourcePosition?.ZeroBasedColumnNumber
|
||||
&& objectProtoypeMappingEntry.OriginalSourcePosition?.ZeroBasedLineNumber == mappingEntry.OriginalSourcePosition?.ZeroBasedLineNumber
|
||||
&& objectName.EndsWith($".{mappingEntry.OriginalName}"))
|
||||
{
|
||||
// The object name already contains the method name, so do not append it
|
||||
methodName = objectName;
|
||||
}
|
||||
else
|
||||
{
|
||||
methodName = $"{objectName}.{mappingEntry.OriginalName}";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -190,7 +190,8 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
|
|||
// Assert
|
||||
Assert.Equal(2, functionMap.Count);
|
||||
|
||||
Assert.Equal("foo.bar", functionMap[0].Bindings[0].Name);
|
||||
Assert.Equal("foo", functionMap[0].Bindings[0].Name);
|
||||
Assert.Equal("bar", functionMap[0].Bindings[1].Name);
|
||||
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.ZeroBasedLineNumber);
|
||||
Assert.Equal(23, functionMap[0].Bindings[0].SourcePosition.ZeroBasedColumnNumber);
|
||||
Assert.Equal(0, functionMap[0].StartSourcePosition.ZeroBasedLineNumber);
|
||||
|
@ -220,7 +221,8 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
|
|||
// Assert
|
||||
Assert.Equal(2, functionMap.Count);
|
||||
|
||||
Assert.Equal("foo.prototype.bar", functionMap[0].Bindings[0].Name);
|
||||
Assert.Equal("foo.prototype", functionMap[0].Bindings[0].Name);
|
||||
Assert.Equal("bar", functionMap[0].Bindings[1].Name);
|
||||
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.ZeroBasedLineNumber);
|
||||
Assert.Equal(23, functionMap[0].Bindings[0].SourcePosition.ZeroBasedColumnNumber);
|
||||
Assert.Equal(0, functionMap[0].StartSourcePosition.ZeroBasedLineNumber);
|
||||
|
@ -250,7 +252,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
|
|||
// Assert
|
||||
Assert.Equal(2, functionMap.Count);
|
||||
|
||||
Assert.Equal("foo.prototype", functionMap[0].Bindings[0].Name);
|
||||
Assert.Equal("foo", functionMap[0].Bindings[0].Name);
|
||||
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.ZeroBasedLineNumber);
|
||||
Assert.Equal(23, functionMap[0].Bindings[0].SourcePosition.ZeroBasedColumnNumber);
|
||||
Assert.Equal("bar", functionMap[0].Bindings[1].Name);
|
||||
|
@ -305,7 +307,8 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
|
|||
// Assert
|
||||
Assert.Equal(2, functionMap.Count);
|
||||
|
||||
Assert.Equal("foo.bar", functionMap[0].Bindings[0].Name);
|
||||
Assert.Equal("foo", functionMap[0].Bindings[0].Name);
|
||||
Assert.Equal("bar", functionMap[0].Bindings[1].Name);
|
||||
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.ZeroBasedLineNumber);
|
||||
Assert.Equal(23, functionMap[0].Bindings[0].SourcePosition.ZeroBasedColumnNumber);
|
||||
Assert.Equal(0, functionMap[0].StartSourcePosition.ZeroBasedLineNumber);
|
||||
|
@ -335,7 +338,8 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
|
|||
// Assert
|
||||
Assert.Equal(2, functionMap.Count);
|
||||
|
||||
Assert.Equal("foo.prototype.bar", functionMap[0].Bindings[0].Name);
|
||||
Assert.Equal("foo.prototype", functionMap[0].Bindings[0].Name);
|
||||
Assert.Equal("bar", functionMap[0].Bindings[1].Name);
|
||||
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.ZeroBasedLineNumber);
|
||||
Assert.Equal(23, functionMap[0].Bindings[0].SourcePosition.ZeroBasedColumnNumber);
|
||||
Assert.Equal(0, functionMap[0].StartSourcePosition.ZeroBasedLineNumber);
|
||||
|
@ -365,7 +369,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
|
|||
// Assert
|
||||
Assert.Equal(2, functionMap.Count);
|
||||
|
||||
Assert.Equal("foo.prototype", functionMap[0].Bindings[0].Name);
|
||||
Assert.Equal("foo", functionMap[0].Bindings[0].Name);
|
||||
Assert.Equal(0, functionMap[0].Bindings[0].SourcePosition.ZeroBasedLineNumber);
|
||||
Assert.Equal(23, functionMap[0].Bindings[0].SourcePosition.ZeroBasedColumnNumber);
|
||||
Assert.Equal("bar", functionMap[0].Bindings[1].Name);
|
||||
|
|
|
@ -97,6 +97,7 @@
|
|||
<Compile Include="KeyValueCacheUnitTests.cs" />
|
||||
<Compile Include="StackFrameDeminifierUnitTests.cs" />
|
||||
<Compile Include="StackTraceDeminifierClosureEndToEndTests.cs" />
|
||||
<Compile Include="StackTraceDeminifierWebpackEndToEndTests.cs" />
|
||||
<Compile Include="StackTraceDeminifierEndToEndTests.cs" />
|
||||
<Compile Include="StackTraceDeminifierUnitTests.cs" />
|
||||
<Compile Include="StackTraceParserUnitTests.cs" />
|
||||
|
@ -150,6 +151,13 @@
|
|||
<Error Condition="!Exists('..\..\packages\xunit.core.2.4.1\build\xunit.core.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.core.2.4.1\build\xunit.core.props'))" />
|
||||
<Error Condition="!Exists('..\..\packages\xunit.core.2.4.1\build\xunit.core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.core.2.4.1\build\xunit.core.targets'))" />
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
<ItemGroup>
|
||||
<TestFiles Include="$(ProjectDir)webpackapp\*.*" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(TestFiles)" DestinationFolder="$(OutDir)webpackapp\">
|
||||
</Copy>
|
||||
</Target>
|
||||
<Import Project="..\..\packages\xunit.core.2.4.1\build\xunit.core.targets" Condition="Exists('..\..\packages\xunit.core.2.4.1\build\xunit.core.targets')" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
|
|||
Assert.Equal("mynamespace.objectWithMethods.propertyMethodLevel2", results.DeminifiedStackFrameResults[0].DeminifiedStackFrame.MethodName);
|
||||
Assert.Equal("mynamespace.objectWithMethods.prototypeMethodLevel1", results.DeminifiedStackFrameResults[1].DeminifiedStackFrame.MethodName);
|
||||
Assert.Equal("GlobalFunction", results.DeminifiedStackFrameResults[2].DeminifiedStackFrame.MethodName);
|
||||
Assert.Equal("window", results.DeminifiedStackFrameResults[3].DeminifiedStackFrame.MethodName);
|
||||
Assert.Equal("window.onload", results.DeminifiedStackFrameResults[3].DeminifiedStackFrame.MethodName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace SourcemapToolkit.CallstackDeminifier.UnitTests
|
|||
Assert.Equal("level2", results.DeminifiedStackFrameResults[2].DeminifiedStackFrame.MethodName);
|
||||
Assert.Equal("level1", results.DeminifiedStackFrameResults[3].DeminifiedStackFrame.MethodName);
|
||||
Assert.Equal("causeCrash", results.DeminifiedStackFrameResults[4].DeminifiedStackFrame.MethodName);
|
||||
Assert.Equal("window", results.DeminifiedStackFrameResults[5].DeminifiedStackFrame.MethodName);
|
||||
Assert.Equal("window.onload", results.DeminifiedStackFrameResults[5].DeminifiedStackFrame.MethodName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -132,7 +132,7 @@ window.onload/<@http://localhost:11323/crashcauser.min.js:1:445";
|
|||
at level2 in crashcauser.js:10:8
|
||||
at level1 in crashcauser.js:5:8
|
||||
at causeCrash in crashcauser.js:27:4
|
||||
at window in crashcauser.js:32:8";
|
||||
at window.onload in crashcauser.js:32:8";
|
||||
|
||||
// Act
|
||||
string formatted = results.ToString();
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
using Xunit;
|
||||
using System.IO;
|
||||
using System;
|
||||
|
||||
namespace SourcemapToolkit.CallstackDeminifier.UnitTests
|
||||
{
|
||||
public class WebpackTestProvider : ISourceMapProvider, ISourceCodeProvider
|
||||
{
|
||||
private static readonly string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "webpackapp");
|
||||
|
||||
private StreamReader GetStreamOrNull(string fileName)
|
||||
{
|
||||
string filePath = Path.Combine(basePath, fileName);
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
return new StreamReader(File.OpenRead(filePath));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public StreamReader GetSourceCode(string sourceUrl)
|
||||
{
|
||||
return GetStreamOrNull(Path.GetFileName(sourceUrl));
|
||||
}
|
||||
|
||||
public StreamReader GetSourceMapContentsForCallstackUrl(string sourceUrl)
|
||||
{
|
||||
return GetStreamOrNull($"{Path.GetFileName(sourceUrl)}.map");
|
||||
}
|
||||
}
|
||||
|
||||
public class StackTraceDeminifierWebpackEndToEndTests
|
||||
{
|
||||
private StackTraceDeminifier GetStackTraceDeminifierWithDependencies()
|
||||
{
|
||||
var provider = new WebpackTestProvider();
|
||||
return StackTraceDeminfierFactory.GetStackTraceDeminfier(provider, provider);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeminifyStackTrace_MinifiedStackTrace_CorrectDeminificationWhenPossible()
|
||||
{
|
||||
// Arrange
|
||||
StackTraceDeminifier stackTraceDeminifier = GetStackTraceDeminifierWithDependencies();
|
||||
string chromeStackTrace = @"TypeError: Cannot read property 'nonExistantmember' of undefined
|
||||
at t.onButtonClick (http://localhost:3000/js/bundle.ffe51781aee314a37903.min.js:1:3573)
|
||||
at Object.sh (https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js:164:410)";
|
||||
string deminifiedStackTrace = @"TypeError: Cannot read property 'nonExistantmember' of undefined
|
||||
at _this.onButtonClick in webpack:///./components/App.tsx:10:45
|
||||
at Object.sh in https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js:163:409";
|
||||
|
||||
// Act
|
||||
DeminifyStackTraceResult results = stackTraceDeminifier.DeminifyStackTrace(chromeStackTrace);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(deminifiedStackTrace.Replace("\r", ""), results.ToString().Replace("\r", ""));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
# Example Webpack App Source
|
||||
|
||||
This is an example of minified JS from a Webpack + TypeScript + React app.
|
||||
|
||||
It was generated from https://github.com/vikpe/react-webpack-typescript-starter
|
2
tests/SourcemapToolkit.CallstackDeminifier.UnitTests/webpackapp/bundle.ffe51781aee314a37903.min.js
поставляемый
Normal file
2
tests/SourcemapToolkit.CallstackDeminifier.UnitTests/webpackapp/bundle.ffe51781aee314a37903.min.js
поставляемый
Normal file
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Загрузка…
Ссылка в новой задаче