提交 489f9e36 编写于 作者: T Tomas Matousek

[Portable PDB] Implement GetMethodFromDocumentPositionm, GetMethodsInDocument...

[Portable PDB] Implement GetMethodFromDocumentPositionm, GetMethodsInDocument and GetSourceExtentInDocument APIs
上级 3c0ac0db
......@@ -26,6 +26,10 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" />
<ItemGroup>
<ProjectReference Include="..\..\Test\PdbUtilities\PdbUtilities.csproj">
<Project>{afde6bea-5038-4a4a-a88e-dbd2e4088eed}</Project>
<Name>PdbUtilities</Name>
</ProjectReference>
<ProjectReference Include="..\Microsoft.DiaSymReader.PortablePdb\Microsoft.DiaSymReader.PortablePdb.csproj">
<Project>{f83343ba-b4ea-451c-b6db-5d645e6171bc}</Project>
<Name>Microsoft.DiaSymReader.PortablePdb</Name>
......@@ -144,16 +148,34 @@
<EmbeddedResource Include="Resources\Scopes.pdbx">
<LogicalName>Scopes.pdbx</LogicalName>
</EmbeddedResource>
<None Include="Resources\build.cmd" />
<None Include="Resources\Scopes.cs" />
<None Include="Resources\Async.cs" />
<EmbeddedResource Include="Resources\MethodBoundaries.dll">
<LogicalName>MethodBoundaries.dll</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\MethodBoundaries.pdb">
<LogicalName>MethodBoundaries.pdb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\MethodBoundaries.dllx">
<LogicalName>MethodBoundaries.dllx</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Resources\MethodBoundaries.pdbx">
<LogicalName>MethodBoundaries.pdbx</LogicalName>
</EmbeddedResource>
<Content Include="Resources\Documents.cmd" />
<Content Include="Resources\Scopes.cmd" />
<Content Include="Resources\MethodBoundaries.cmd" />
<Content Include="Resources\Async.cmd" />
<Content Include="Resources\Scopes.cs" />
<Content Include="Resources\Async.cs" />
<Content Include="Resources\Documents.cs" />
<Compile Include="..\..\Compilers\Core\Portable\InternalUtilities\ComStreamWrapper.cs">
<Link>TestHelpers\ComStreamWrapper.cs</Link>
</Compile>
<Compile Include="MethodMapTests.cs" />
<Compile Include="ResourceLoader.cs" />
<Content Include="Resources\MethodBoundaries.cs" />
<Compile Include="SymBinderTests.cs" />
<Compile Include="TestHelpers\SymTestHelpers.cs" />
<Compile Include="TestResources.cs" />
<Compile Include="..\..\Compilers\Core\Portable\InternalUtilities\ComStreamWrapper.cs">
<Link>TestHelpers\ComStreamWrapper.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="TestHelpers\AssertEx.cs" />
......
csc /target:library /debug+ /features:pdb=portable /optimize- /features:deterministic Async.cs
copy /y Async.pdb Async.pdbx
del Async.pdb
csc /target:library /debug+ /features:pdb=portable /optimize- /features:deterministic Documents.cs
copy /y Documents.pdb Documents.pdbx
del Documents.pdb
csc /target:library /debug+ /optimize- /features:pdb=portable /features:deterministic MethodBoundaries.cs
copy /y MethodBoundaries.pdb MethodBoundaries.pdbx
copy /y MethodBoundaries.dll MethodBoundaries.dllx
csc /target:library /debug+ /optimize- /features:deterministic MethodBoundaries.cs
#line 1 "C:\MethodBoundaries1.cs"
#pragma checksum "C:\MethodBoundaries1.cs" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "DBEB2A067B2F0E0D678A002C587A2806056C3DCE"
partial class C
{
int i = F() // 6
+ // (7)
F(); // (8)
public C() // 10
{ // 11
F(); // 12
} // 13
int j = F(); // 15
public static int F()
{ // 18
#line 10 "C:\MethodBoundaries1.cs"
F(); // 11
#line 5 "C:\MethodBoundaries1.cs"
F(); // 6
F(); // 8
F(); // 9
#line 5 "C:\MethodBoundaries1.cs"
F(); // 6
#line 1 "C:\MethodBoundaries2.cs"
F(); // 2-2
#line 20 "C:\MethodBoundaries1.cs"
F(); // 21
return 1; // 23
} // 24
public static int G()
#line 4 "C:\MethodBoundaries1.cs"
{ // 5
F( // 6
// (7)
); // (8)
return 1; // 9
} // 10
#line 5 "C:\MethodBoundaries2.cs"
public static int E0() => F(); // 6
#line 7 "C:\MethodBoundaries2.cs"
public static int E1() => F(); // 8
public static int H()
#line 4 "C:\MethodBoundaries2.cs"
{ // 5
F( // 6
// (7)
// (8)
); // (9)
return 1; // 10
} // 11
#line 6 "C:\MethodBoundaries2.cs"
public static int E2() => F(); // 7
public static int E3() =>
#line 8 "C:\MethodBoundaries2.cs"
F() + // 9
F(); // (10)
public static int E4() =>
#line 9 "C:\MethodBoundaries2.cs"
F(); // 10
// Overlapping sequence point spans from different methods.
public static void J1()
#line 13 "C:\MethodBoundaries2.cs"
{ // 14
F(); // 15
} // 16
public static int I()
#line 11 "C:\MethodBoundaries2.cs"
{ // 12
F( // 13
// (14) overlaps with J1
// (15) overlaps with J1
// (16) overlaps with J1
// (17) overlaps with J2
// (18) overlaps with J2
// (19)
// (20)
// (21)
); // (22)
return 1; // 23
} // 24
public static void J2()
#line 16 "C:\MethodBoundaries2.cs"
{ // 17
F(); // 18
#line 28 "C:\MethodBoundaries2.cs"
} // 29
public static void K1()
#line 1 "C:\MethodBoundaries3.cs"
{ // 2 K1
F( // 3 K1
// (4) K1, K2
// (5) K1, K2
// (6) K1, K2, K3
// (7) K1, K2, K3
// (8) K1, K2, K3, K4
// (9) K1, K2, K3, K4
// (10) K1, K2, K3
// (11) K1, K2, K3
); // (12) K1, K2
} // 13 K1
public static void K2()
#line 3 "C:\MethodBoundaries3.cs"
{ // 4
F( // 5
// (6)
// (7)
// (8)
// (9)
// (10)
); // (11)
} // 12
public static void K3()
#line 5 "C:\MethodBoundaries3.cs"
{ // 6
F( // 7
// (8)
// (9)
); // (10)
} // 11
public static void K4() =>
#line 7 "C:\MethodBoundaries3.cs"
F( // 8
); // (9)
}
\ No newline at end of file
csc /target:library /debug+ /features:pdb=portable /optimize- /features:deterministic Async.cs
copy /y Async.pdb Async.pdbx
del Async.pdb
csc /target:library /debug:portable /optimize- /features:deterministic Scopes.cs
copy /y Scopes.pdb Scopes.pdbx
del Scopes.pdb
csc /target:library /debug:portable /optimize- /features:deterministic Documents.cs
copy /y Documents.pdb Documents.pdbx
del Documents.pdb
csc /target:library /debug:portable /optimize- /features:deterministic Async.cs
copy /y Async.pdb Async.pdbx
del Async.pdb
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Roslyn.Test.Utilities;
using System;
using System.Reflection.Metadata;
using Xunit;
using System.IO;
using System.Collections.Generic;
namespace Microsoft.DiaSymReader.PortablePdb.UnitTests
{
using static SymTestHelpers;
public class SymReaderTests
{
#region Helpers
private static SymReader CreateSymReaderFromResource(KeyValuePair<byte[], byte[]> peAndPdb)
{
MetadataReader mdReader;
return CreateSymReaderFromResource(peAndPdb, out mdReader);
}
private static SymReader CreateSymReaderFromResource(KeyValuePair<byte[], byte[]> peAndPdb, out MetadataReader mdReader)
{
var importer = new SymMetadataImport(new MemoryStream(peAndPdb.Key));
mdReader = importer.MetadataReader;
return new SymReader(new PortablePdbReader(peAndPdb.Value, peAndPdb.Value.Length, importer));
}
private void ValidateDocumentUrl(ISymUnmanagedDocument document, string url)
{
int actualCount, actualCount2;
Assert.Equal(HResult.S_OK, document.GetUrl(0, out actualCount, null));
char[] actualUrl = new char[actualCount];
Assert.Equal(HResult.S_OK, document.GetUrl(actualCount, out actualCount2, actualUrl));
Assert.Equal(url, new string(actualUrl, 0, actualUrl.Length - 1));
}
private void ValidateDocument(ISymUnmanagedDocument document, string url, string algorithmId, byte[] checksum)
{
ValidateDocumentUrl(document, url);
int actualCount, actualCount2;
if (checksum != null)
{
Assert.Equal(HResult.S_OK, document.GetChecksum(0, out actualCount, null));
byte[] actualChecksum = new byte[actualCount];
Assert.Equal(HResult.S_OK, document.GetChecksum(actualCount, out actualCount2, actualChecksum));
Assert.Equal(actualCount, actualCount2);
AssertEx.Equal(checksum, actualChecksum);
}
else
{
Assert.Equal(HResult.S_FALSE, document.GetChecksum(0, out actualCount, null));
Assert.Equal(0, actualCount);
}
var guid = Guid.NewGuid();
Assert.Equal(HResult.S_OK, document.GetChecksumAlgorithmId(ref guid));
Assert.Equal(algorithmId != null ? new Guid(algorithmId) : default(Guid), guid);
guid = Guid.NewGuid();
Assert.Equal(HResult.S_OK, document.GetLanguageVendor(ref guid));
Assert.Equal(new Guid("994b45c4-e6e9-11d2-903f-00c04fa302a1"), guid);
guid = Guid.NewGuid();
Assert.Equal(HResult.S_OK, document.GetDocumentType(ref guid));
Assert.Equal(new Guid("5a869d0b-6611-11d3-bd2a-0000f80849bd"), guid);
}
private void ValidateRange(ISymUnmanagedScope scope, int expectedStartOffset, int expectedLength)
{
int actualOffset;
Assert.Equal(HResult.S_OK, scope.GetStartOffset(out actualOffset));
Assert.Equal(expectedStartOffset, actualOffset);
Assert.Equal(HResult.S_OK, scope.GetEndOffset(out actualOffset));
Assert.Equal(expectedStartOffset + expectedLength, 2);
}
private void ValidateConstant(ISymUnmanagedConstant constant, string name, object value, byte[] signature)
{
int length, length2;
// name:
Assert.Equal(HResult.S_OK, constant.GetName(0, out length, null));
Assert.Equal(name.Length + 1, length);
var actualName = new char[length];
Assert.Equal(HResult.S_OK, constant.GetName(length, out length2, actualName));
Assert.Equal(length, length2);
Assert.Equal(name + "\0", new string(actualName));
// value:
object actualValue;
Assert.Equal(HResult.S_OK, constant.GetValue(out actualValue));
Assert.Equal(value, actualValue);
// signature:
Assert.Equal(HResult.S_OK, constant.GetSignature(0, out length, null));
var actualSignature = new byte[length];
Assert.Equal(HResult.S_OK, constant.GetSignature(length, out length2, actualSignature));
Assert.Equal(length, length2);
AssertEx.Equal(signature, actualSignature);
}
private void ValidateVariable(ISymUnmanagedVariable variable, string name, int slot, LocalVariableAttributes attributes, byte[] signature)
{
int length, length2;
// name:
Assert.Equal(HResult.S_OK, variable.GetName(0, out length, null));
Assert.Equal(name.Length + 1, length);
var actualName = new char[length];
Assert.Equal(HResult.S_OK, variable.GetName(length, out length2, actualName));
Assert.Equal(length, length2);
Assert.Equal(name + "\0", new string(actualName));
int value;
Assert.Equal(HResult.S_OK, variable.GetAddressField1(out value));
Assert.Equal(slot, value);
Assert.Equal(HResult.E_NOTIMPL, variable.GetAddressField2(out value));
Assert.Equal(HResult.E_NOTIMPL, variable.GetAddressField3(out value));
Assert.Equal(HResult.E_NOTIMPL, variable.GetStartOffset(out value));
Assert.Equal(HResult.E_NOTIMPL, variable.GetEndOffset(out value));
Assert.Equal(HResult.S_OK, variable.GetAttributes(out value));
Assert.Equal(attributes, (LocalVariableAttributes)value);
Assert.Equal(HResult.S_OK, variable.GetAddressKind(out value));
Assert.Equal(1, value);
Assert.Equal(HResult.S_OK, variable.GetSignature(0, out length, null));
var actualSignature = new byte[length];
Assert.Equal(HResult.S_OK, variable.GetSignature(length, out length2, actualSignature));
Assert.Equal(length, length2);
AssertEx.Equal(signature, actualSignature);
}
private void ValidateRootScope(ISymUnmanagedScope scope)
{
int count;
Assert.Equal(HResult.S_OK, scope.GetLocalCount(out count));
Assert.Equal(0, count);
Assert.Equal(HResult.S_OK, ((ISymUnmanagedScope2)scope).GetConstantCount(out count));
Assert.Equal(0, count);
Assert.Equal(HResult.S_OK, ((ISymUnmanagedScope2)scope).GetNamespaces(0, out count, null));
Assert.Equal(0, count);
ISymUnmanagedScope parent;
Assert.Equal(HResult.S_OK, scope.GetParent(out parent));
Assert.Null(parent);
}
private ISymUnmanagedScope[] GetAndValidateChildScopes(ISymUnmanagedScope scope, int expectedCount)
{
int count, count2;
Assert.Equal(HResult.S_OK, scope.GetChildren(0, out count, null));
Assert.Equal(expectedCount, count);
var children = new ISymUnmanagedScope[count];
Assert.Equal(HResult.S_OK, scope.GetChildren(count, out count2, children));
Assert.Equal(count, count2);
return children;
}
private ISymUnmanagedConstant[] GetAndValidateConstants(ISymUnmanagedScope scope, int expectedCount)
{
int count, count2;
Assert.Equal(HResult.S_OK, ((ISymUnmanagedScope2)scope).GetConstants(0, out count, null));
Assert.Equal(expectedCount, count);
var constants = new ISymUnmanagedConstant[count];
Assert.Equal(HResult.S_OK, ((ISymUnmanagedScope2)scope).GetConstants(count, out count2, constants));
Assert.Equal(count, count2);
return constants;
}
private ISymUnmanagedVariable[] GetAndValidateVariables(ISymUnmanagedScope scope, int expectedCount)
{
int count, count2, count3;
Assert.Equal(HResult.S_OK, scope.GetLocalCount(out count));
Assert.Equal(expectedCount, count);
Assert.Equal(HResult.S_OK, scope.GetLocals(0, out count2, null));
Assert.Equal(expectedCount, count2);
var variables = new ISymUnmanagedVariable[count];
Assert.Equal(HResult.S_OK, scope.GetLocals(count, out count3, variables));
Assert.Equal(count, count3);
return variables;
}
private void ValidateAsyncMethod(ISymUnmanagedReader symReader, int moveNextMethodToken, int kickoffMethodToken, int catchHandlerOffset, int[] yieldOffsets, int[] resumeOffsets)
{
ISymUnmanagedMethod method;
Assert.Equal(HResult.S_OK, symReader.GetMethod(moveNextMethodToken, out method));
var asyncMethod = (ISymUnmanagedAsyncMethod)method;
bool isAsync;
Assert.Equal(HResult.S_OK, asyncMethod.IsAsyncMethod(out isAsync));
Assert.True(isAsync);
int actualKickoffMethodToken;
Assert.Equal(HResult.S_OK, asyncMethod.GetKickoffMethod(out actualKickoffMethodToken));
Assert.Equal(kickoffMethodToken, actualKickoffMethodToken);
bool hasCatchHandlerILOffset;
Assert.Equal(HResult.S_OK, asyncMethod.HasCatchHandlerILOffset(out hasCatchHandlerILOffset));
Assert.Equal(catchHandlerOffset >= 0, hasCatchHandlerILOffset);
int actualCatchHandlerOffset;
if (hasCatchHandlerILOffset)
{
Assert.Equal(HResult.S_OK, asyncMethod.GetCatchHandlerILOffset(out actualCatchHandlerOffset));
Assert.Equal(catchHandlerOffset, actualCatchHandlerOffset);
}
else
{
Assert.Equal(HResult.E_UNEXPECTED, asyncMethod.GetCatchHandlerILOffset(out actualCatchHandlerOffset));
}
int count, count2;
Assert.Equal(HResult.S_OK, asyncMethod.GetAsyncStepInfoCount(out count));
Assert.Equal(yieldOffsets.Length, count);
Assert.Equal(resumeOffsets.Length, count);
var actualYieldOffsets = new int[count];
var actualResumeOffsets = new int[count];
var actualResumeMethods = new int[count];
Assert.Equal(HResult.S_OK, asyncMethod.GetAsyncStepInfo(count, out count2, actualYieldOffsets, actualResumeOffsets, actualResumeMethods));
AssertEx.Equal(yieldOffsets, actualYieldOffsets);
AssertEx.Equal(resumeOffsets, actualResumeOffsets);
foreach (int actualResumeMethod in actualResumeMethods)
{
Assert.Equal(moveNextMethodToken, actualResumeMethod);
}
}
#endregion
[Fact]
public unsafe void TestMetadataHeaders1()
{
......@@ -270,8 +39,8 @@ public void TestGetDocuments1()
Assert.Equal(11, actualCount2);
ValidateDocument(actualDocuments[0],
url: @"C:\Documents.cs",
algorithmId: "ff1816ec-aa5e-4d10-87f7-6f4963833460",
url: @"C:\Documents.cs",
algorithmId: "ff1816ec-aa5e-4d10-87f7-6f4963833460",
checksum: new byte[] { 0xDB, 0xEB, 0x2A, 0x06, 0x7B, 0x2F, 0x0E, 0x0D, 0x67, 0x8A, 0x00, 0x2C, 0x58, 0x7A, 0x28, 0x06, 0x05, 0x6C, 0x3D, 0xCE });
ValidateDocument(actualDocuments[1], url: @"C:\a\b\c\d\1.cs", algorithmId: null, checksum: null);
......@@ -302,7 +71,7 @@ public void TestGetDocument1()
TestGetDocument(symReader, @":6.cs", expectedUrl: @":6.cs");
}
private void TestGetDocument(SymReader symReader, string name, string expectedUrl)
private void TestGetDocument(ISymUnmanagedReader symReader, string name, string expectedUrl)
{
ISymUnmanagedDocument document;
if (expectedUrl != null)
......@@ -400,7 +169,7 @@ public void TestMethods1()
ValidateConstant(constants[23], "NullObject", 0, new byte[] { 0x1c });
ValidateConstant(constants[24], "NullDynamic", 0, new byte[] { 0x1c });
// Note: Natvie PDBs produce expanded form of the signature stored as StandAloneSig.
// Note: Native PDBs produce expanded form of the signature stored as StandAloneSig.
// In Portable PDBs we produce a TypeSpec. Since a StandAlongSig can also contain a TypeSpec
// the consumers should be able to resolve it. If we find a case where that's not true we can
// potentially expand the TypeSpec signature in ISymUnmanagedConstant.GetValue.
......@@ -468,11 +237,11 @@ public void TestAsyncMethods()
var symReader = CreateSymReaderFromResource(TestResources.Async.DllAndPdb);
ValidateAsyncMethod(
symReader,
symReader,
moveNextMethodToken: 0x06000005,
kickoffMethodToken: 0x06000001,
catchHandlerOffset: -1,
yieldOffsets: new[] { 0x46, 0xAF, 0x11A },
yieldOffsets: new[] { 0x46, 0xAF, 0x11A },
resumeOffsets: new[] { 0x64, 0xCE, 0x136 });
ValidateAsyncMethod(
......
......@@ -150,7 +150,7 @@ public static void AreEqual<T>(T expected, T actual, string message = null, IEqu
}
}
public static void Equal<T>(ImmutableArray<T> expected, IEnumerable<T> actual, IEqualityComparer<T> comparer = null, string message = null)
public static void Equal<T>(ImmutableArray<T> expected, IEnumerable<T> actual, Func<T, T, bool> comparer = null, string message = null)
{
if (actual == null || expected.IsDefault)
{
......@@ -162,7 +162,7 @@ public static void Equal<T>(ImmutableArray<T> expected, IEnumerable<T> actual, I
}
}
public static void Equal<T>(IEnumerable<T> expected, ImmutableArray<T> actual, IEqualityComparer<T> comparer = null, string message = null, string itemSeparator = null)
public static void Equal<T>(IEnumerable<T> expected, ImmutableArray<T> actual, Func<T, T, bool> comparer = null, string message = null, string itemSeparator = null)
{
if (expected == null || actual.IsDefault)
{
......@@ -174,12 +174,12 @@ public static void Equal<T>(IEnumerable<T> expected, ImmutableArray<T> actual, I
}
}
public static void Equal<T>(ImmutableArray<T> expected, ImmutableArray<T> actual, IEqualityComparer<T> comparer = null, string message = null, string itemSeparator = null)
public static void Equal<T>(ImmutableArray<T> expected, ImmutableArray<T> actual, Func<T, T, bool> comparer = null, string message = null, string itemSeparator = null)
{
Equal(expected, (IEnumerable<T>)actual, comparer, message, itemSeparator);
}
public static void Equal<T>(IEnumerable<T> expected, IEnumerable<T> actual, IEqualityComparer<T> comparer = null, string message = null,
public static void Equal<T>(IEnumerable<T> expected, IEnumerable<T> actual, Func<T, T, bool> comparer = null, string message = null,
string itemSeparator = null, Func<T, string> itemInspector = null)
{
if (ReferenceEquals(expected, actual))
......@@ -208,7 +208,7 @@ public static void Equal<T>(ImmutableArray<T> expected, ImmutableArray<T> actual
}
}
private static bool SequenceEqual<T>(IEnumerable<T> expected, IEnumerable<T> actual, IEqualityComparer<T> comparer = null)
private static bool SequenceEqual<T>(IEnumerable<T> expected, IEnumerable<T> actual, Func<T, T, bool> comparer = null)
{
var enumerator1 = expected.GetEnumerator();
var enumerator2 = actual.GetEnumerator();
......@@ -231,7 +231,7 @@ private static bool SequenceEqual<T>(IEnumerable<T> expected, IEnumerable<T> act
var value1 = enumerator1.Current;
var value2 = enumerator2.Current;
if (!(comparer != null ? comparer.Equals(value1, value2) : AssertEqualityComparer<T>.Equals(value1, value2)))
if (!(comparer != null ? comparer(value1, value2) : AssertEqualityComparer<T>.Equals(value1, value2)))
{
return false;
}
......@@ -435,7 +435,7 @@ public static string GetAssertMessage<T>(IEnumerable<T> expected, IEnumerable<T>
public static string GetAssertMessage<T>(
IEnumerable<T> expected,
IEnumerable<T> actual,
IEqualityComparer<T> comparer = null,
Func<T, T, bool> comparer = null,
Func<T, string> itemInspector = null,
string itemSeparator = null,
string expectedValueSourcePath = null,
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection.Metadata;
using Roslyn.Test.PdbUtilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.DiaSymReader.PortablePdb.UnitTests
{
internal static class SymTestHelpers
{
public static ISymUnmanagedReader CreateSymReaderFromResource(KeyValuePair<byte[], byte[]> peAndPdb)
{
return SymReaderFactory.CreateReader(new MemoryStream(peAndPdb.Value), new SymMetadataImport(new MemoryStream(peAndPdb.Key)));
}
public static void ValidateDocumentUrl(ISymUnmanagedDocument document, string url)
{
int actualCount, actualCount2;
Assert.Equal(HResult.S_OK, document.GetUrl(0, out actualCount, null));
char[] actualUrl = new char[actualCount];
Assert.Equal(HResult.S_OK, document.GetUrl(actualCount, out actualCount2, actualUrl));
Assert.Equal(url, new string(actualUrl, 0, actualUrl.Length - 1));
}
public static void ValidateDocument(ISymUnmanagedDocument document, string url, string algorithmId, byte[] checksum)
{
ValidateDocumentUrl(document, url);
int actualCount, actualCount2;
if (checksum != null)
{
Assert.Equal(HResult.S_OK, document.GetChecksum(0, out actualCount, null));
byte[] actualChecksum = new byte[actualCount];
Assert.Equal(HResult.S_OK, document.GetChecksum(actualCount, out actualCount2, actualChecksum));
Assert.Equal(actualCount, actualCount2);
AssertEx.Equal(checksum, actualChecksum);
}
else
{
Assert.Equal(HResult.S_FALSE, document.GetChecksum(0, out actualCount, null));
Assert.Equal(0, actualCount);
}
var guid = Guid.NewGuid();
Assert.Equal(HResult.S_OK, document.GetChecksumAlgorithmId(ref guid));
Assert.Equal(algorithmId != null ? new Guid(algorithmId) : default(Guid), guid);
guid = Guid.NewGuid();
Assert.Equal(HResult.S_OK, document.GetLanguageVendor(ref guid));
Assert.Equal(new Guid("994b45c4-e6e9-11d2-903f-00c04fa302a1"), guid);
guid = Guid.NewGuid();
Assert.Equal(HResult.S_OK, document.GetDocumentType(ref guid));
Assert.Equal(new Guid("5a869d0b-6611-11d3-bd2a-0000f80849bd"), guid);
}
public static void ValidateRange(ISymUnmanagedScope scope, int expectedStartOffset, int expectedLength)
{
int actualOffset;
Assert.Equal(HResult.S_OK, scope.GetStartOffset(out actualOffset));
Assert.Equal(expectedStartOffset, actualOffset);
Assert.Equal(HResult.S_OK, scope.GetEndOffset(out actualOffset));
Assert.Equal(expectedStartOffset + expectedLength, 2);
}
public static void ValidateConstant(ISymUnmanagedConstant constant, string name, object value, byte[] signature)
{
int length, length2;
// name:
Assert.Equal(HResult.S_OK, constant.GetName(0, out length, null));
Assert.Equal(name.Length + 1, length);
var actualName = new char[length];
Assert.Equal(HResult.S_OK, constant.GetName(length, out length2, actualName));
Assert.Equal(length, length2);
Assert.Equal(name + "\0", new string(actualName));
// value:
object actualValue;
Assert.Equal(HResult.S_OK, constant.GetValue(out actualValue));
Assert.Equal(value, actualValue);
// signature:
Assert.Equal(HResult.S_OK, constant.GetSignature(0, out length, null));
var actualSignature = new byte[length];
Assert.Equal(HResult.S_OK, constant.GetSignature(length, out length2, actualSignature));
Assert.Equal(length, length2);
AssertEx.Equal(signature, actualSignature);
}
public static void ValidateVariable(ISymUnmanagedVariable variable, string name, int slot, LocalVariableAttributes attributes, byte[] signature)
{
int length, length2;
// name:
Assert.Equal(HResult.S_OK, variable.GetName(0, out length, null));
Assert.Equal(name.Length + 1, length);
var actualName = new char[length];
Assert.Equal(HResult.S_OK, variable.GetName(length, out length2, actualName));
Assert.Equal(length, length2);
Assert.Equal(name + "\0", new string(actualName));
int value;
Assert.Equal(HResult.S_OK, variable.GetAddressField1(out value));
Assert.Equal(slot, value);
Assert.Equal(HResult.E_NOTIMPL, variable.GetAddressField2(out value));
Assert.Equal(HResult.E_NOTIMPL, variable.GetAddressField3(out value));
Assert.Equal(HResult.E_NOTIMPL, variable.GetStartOffset(out value));
Assert.Equal(HResult.E_NOTIMPL, variable.GetEndOffset(out value));
Assert.Equal(HResult.S_OK, variable.GetAttributes(out value));
Assert.Equal(attributes, (LocalVariableAttributes)value);
Assert.Equal(HResult.S_OK, variable.GetAddressKind(out value));
Assert.Equal(1, value);
Assert.Equal(HResult.S_OK, variable.GetSignature(0, out length, null));
var actualSignature = new byte[length];
Assert.Equal(HResult.S_OK, variable.GetSignature(length, out length2, actualSignature));
Assert.Equal(length, length2);
AssertEx.Equal(signature, actualSignature);
}
public static void ValidateRootScope(ISymUnmanagedScope scope)
{
int count;
Assert.Equal(HResult.S_OK, scope.GetLocalCount(out count));
Assert.Equal(0, count);
Assert.Equal(HResult.S_OK, ((ISymUnmanagedScope2)scope).GetConstantCount(out count));
Assert.Equal(0, count);
Assert.Equal(HResult.S_OK, ((ISymUnmanagedScope2)scope).GetNamespaces(0, out count, null));
Assert.Equal(0, count);
ISymUnmanagedScope parent;
Assert.Equal(HResult.S_OK, scope.GetParent(out parent));
Assert.Null(parent);
}
public static ISymUnmanagedScope[] GetAndValidateChildScopes(ISymUnmanagedScope scope, int expectedCount)
{
int count, count2;
Assert.Equal(HResult.S_OK, scope.GetChildren(0, out count, null));
Assert.Equal(expectedCount, count);
var children = new ISymUnmanagedScope[count];
Assert.Equal(HResult.S_OK, scope.GetChildren(count, out count2, children));
Assert.Equal(count, count2);
return children;
}
public static ISymUnmanagedConstant[] GetAndValidateConstants(ISymUnmanagedScope scope, int expectedCount)
{
int count, count2;
Assert.Equal(HResult.S_OK, ((ISymUnmanagedScope2)scope).GetConstants(0, out count, null));
Assert.Equal(expectedCount, count);
var constants = new ISymUnmanagedConstant[count];
Assert.Equal(HResult.S_OK, ((ISymUnmanagedScope2)scope).GetConstants(count, out count2, constants));
Assert.Equal(count, count2);
return constants;
}
public static ISymUnmanagedVariable[] GetAndValidateVariables(ISymUnmanagedScope scope, int expectedCount)
{
int count, count2, count3;
Assert.Equal(HResult.S_OK, scope.GetLocalCount(out count));
Assert.Equal(expectedCount, count);
Assert.Equal(HResult.S_OK, scope.GetLocals(0, out count2, null));
Assert.Equal(expectedCount, count2);
var variables = new ISymUnmanagedVariable[count];
Assert.Equal(HResult.S_OK, scope.GetLocals(count, out count3, variables));
Assert.Equal(count, count3);
return variables;
}
public static void ValidateAsyncMethod(ISymUnmanagedReader symReader, int moveNextMethodToken, int kickoffMethodToken, int catchHandlerOffset, int[] yieldOffsets, int[] resumeOffsets)
{
ISymUnmanagedMethod method;
Assert.Equal(HResult.S_OK, symReader.GetMethod(moveNextMethodToken, out method));
var asyncMethod = (ISymUnmanagedAsyncMethod)method;
bool isAsync;
Assert.Equal(HResult.S_OK, asyncMethod.IsAsyncMethod(out isAsync));
Assert.True(isAsync);
int actualKickoffMethodToken;
Assert.Equal(HResult.S_OK, asyncMethod.GetKickoffMethod(out actualKickoffMethodToken));
Assert.Equal(kickoffMethodToken, actualKickoffMethodToken);
bool hasCatchHandlerILOffset;
Assert.Equal(HResult.S_OK, asyncMethod.HasCatchHandlerILOffset(out hasCatchHandlerILOffset));
Assert.Equal(catchHandlerOffset >= 0, hasCatchHandlerILOffset);
int actualCatchHandlerOffset;
if (hasCatchHandlerILOffset)
{
Assert.Equal(HResult.S_OK, asyncMethod.GetCatchHandlerILOffset(out actualCatchHandlerOffset));
Assert.Equal(catchHandlerOffset, actualCatchHandlerOffset);
}
else
{
Assert.Equal(HResult.E_UNEXPECTED, asyncMethod.GetCatchHandlerILOffset(out actualCatchHandlerOffset));
}
int count, count2;
Assert.Equal(HResult.S_OK, asyncMethod.GetAsyncStepInfoCount(out count));
Assert.Equal(yieldOffsets.Length, count);
Assert.Equal(resumeOffsets.Length, count);
var actualYieldOffsets = new int[count];
var actualResumeOffsets = new int[count];
var actualResumeMethods = new int[count];
Assert.Equal(HResult.S_OK, asyncMethod.GetAsyncStepInfo(count, out count2, actualYieldOffsets, actualResumeOffsets, actualResumeMethods));
AssertEx.Equal(yieldOffsets, actualYieldOffsets);
AssertEx.Equal(resumeOffsets, actualResumeOffsets);
foreach (int actualResumeMethod in actualResumeMethods)
{
Assert.Equal(moveNextMethodToken, actualResumeMethod);
}
}
}
}
......@@ -37,4 +37,22 @@ public static class Async
public static KeyValuePair<byte[], byte[]> DllAndPdb => new KeyValuePair<byte[], byte[]>(Dll, Pdb);
}
public static class MethodBoundaries
{
private static byte[] _portableDll;
public static byte[] PortableDll => ResourceLoader.GetOrCreateResource(ref _portableDll, nameof(MethodBoundaries) + ".dllx");
private static byte[] _portablePdb;
public static byte[] PortablePdb => ResourceLoader.GetOrCreateResource(ref _portablePdb, nameof(MethodBoundaries) + ".pdbx");
private static byte[] _dll;
public static byte[] Dll => ResourceLoader.GetOrCreateResource(ref _dll, nameof(MethodBoundaries) + ".dll");
private static byte[] _pdb;
public static byte[] Pdb => ResourceLoader.GetOrCreateResource(ref _pdb, nameof(MethodBoundaries) + ".pdb");
public static KeyValuePair<byte[], byte[]> PortableDllAndPdb => new KeyValuePair<byte[], byte[]>(PortableDll, PortablePdb);
public static KeyValuePair<byte[], byte[]> DllAndPdb => new KeyValuePair<byte[], byte[]>(Dll, Pdb);
}
}
......@@ -27,6 +27,17 @@ public void Dispose()
_peReader.Dispose();
}
public unsafe int GetSigFromToken(int tkSignature, out byte* ppvSig, out int pcbSig)
{
var signatureHandle = (StandaloneSignatureHandle)MetadataTokens.Handle(tkSignature);
var bytes = MetadataReader.GetBlobBytes(MetadataReader.GetStandaloneSignature(signatureHandle).Signature);
var pinned = GCHandle.Alloc(bytes, GCHandleType.Pinned);
ppvSig = (byte*)pinned.AddrOfPinnedObject();
pcbSig = bytes.Length;
return HResult.S_OK;
}
public void GetTypeDefProps(
int typeDefinition,
[MarshalAs(UnmanagedType.LPWStr), Out]StringBuilder qualifiedName,
......@@ -95,17 +106,6 @@ public void Dispose()
resolutionScope = MetadataTokens.GetToken(typeRef.ResolutionScope);
}
public unsafe int GetSigFromToken(int tkSignature, out byte* ppvSig, out int pcbSig)
{
var signatureHandle = (StandaloneSignatureHandle)MetadataTokens.Handle(tkSignature);
var bytes = MetadataReader.GetBlobBytes(MetadataReader.GetStandaloneSignature(signatureHandle).Signature);
var pinned = GCHandle.Alloc(bytes, GCHandleType.Pinned);
ppvSig = (byte*)pinned.AddrOfPinnedObject();
pcbSig = bytes.Length;
return HResult.S_OK;
}
#region Not Implemented
public void CloseEnum(uint handleEnum)
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using Roslyn.Utilities;
namespace Microsoft.DiaSymReader.PortablePdb
{
internal sealed class MethodMap
{
[DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
internal struct MethodLineExtent
{
internal sealed class MethodComparer : IComparer<MethodLineExtent>
{
public static readonly MethodComparer Instance = new MethodComparer();
public int Compare(MethodLineExtent x, MethodLineExtent y) => HandleComparer.Default.Compare(x.Method, y.Method);
}
internal sealed class MinLineComparer : IComparer<MethodLineExtent>
{
public static readonly MinLineComparer Instance = new MinLineComparer();
public int Compare(MethodLineExtent x, MethodLineExtent y) => x.MinLine - y.MinLine;
}
public readonly MethodBodyHandle Method;
public readonly int MinLine;
public readonly int MaxLine;
public MethodLineExtent(MethodBodyHandle method, int minLine, int maxLine)
{
Method = method;
MinLine = minLine;
MaxLine = maxLine;
}
private string GetDebuggerDisplay() => $"{MetadataTokens.GetRowNumber(Method)}: [{MinLine}-{MaxLine}]";
}
private struct MethodsInDocument
{
// Consider: we could remove the MaxLine from this list and look it up in ExtensByMinLine
public readonly ImmutableArray<MethodLineExtent> ExtentsByMethod;
public readonly ImmutableArray<ImmutableArray<MethodLineExtent>> ExtentsByMinLine;
public MethodsInDocument(ImmutableArray<MethodLineExtent> extentsByMethod, ImmutableArray<ImmutableArray<MethodLineExtent>> extentsByMinLine)
{
ExtentsByMethod = extentsByMethod;
ExtentsByMinLine = extentsByMinLine;
}
}
// For each document holds on a list of method extent runs.
// Each method is represented exactly once.
// Each run is sorted first by method token and then by start line. Extents in each run are non-everlapping.
private readonly IReadOnlyDictionary<DocumentHandle, MethodsInDocument> _methodsByDocument;
public MethodMap(MetadataReader reader)
{
_methodsByDocument = GroupMethods(GetMethodExtents(reader));
}
private static IReadOnlyDictionary<DocumentHandle, MethodsInDocument> GroupMethods(IEnumerable<KeyValuePair<DocumentHandle, MethodLineExtent>> methodExtents)
{
var builder = new Dictionary<DocumentHandle, ImmutableArray<MethodLineExtent>.Builder>();
foreach (var entry in methodExtents)
{
ImmutableArray<MethodLineExtent>.Builder existing;
if (!builder.TryGetValue(entry.Key, out existing))
{
builder[entry.Key] = existing = ImmutableArray.CreateBuilder<MethodLineExtent>();
}
existing.Add(entry.Value);
}
var result = new Dictionary<DocumentHandle, MethodsInDocument>(builder.Count);
foreach (var entry in builder)
{
var extents = entry.Value;
Debug.Assert(extents.Count > 0);
// sort by method handle:
extents.Sort(MethodLineExtent.MethodComparer.Instance);
// merge spans belonging to a single method:
int j = 0;
for (int i = 1; i < extents.Count; i++)
{
if (extents[i].Method == extents[j].Method)
{
extents[j] = new MethodLineExtent(extents[i].Method, Math.Min(extents[i].MinLine, extents[j].MinLine), Math.Max(extents[i].MaxLine, extents[j].MaxLine));
}
else
{
j++;
if (j < i)
{
extents[j] = extents[i];
}
}
}
Debug.Assert(j < extents.Count);
extents.Count = j + 1;
var extentsByMethod = extents.ToImmutable();
// sort by start line:
extents.Sort(MethodLineExtent.MinLineComparer.Instance);
result.Add(entry.Key, new MethodsInDocument(extentsByMethod, PartitionToNonOverlappingSubsequences(extents)));
}
return result;
}
private static ImmutableArray<ImmutableArray<MethodLineExtent>> PartitionToNonOverlappingSubsequences(ImmutableArray<MethodLineExtent>.Builder extentsOrderedByMinLine)
{
// Most of the time method extents are non-overlapping. Only extents of anonymous methods and queries overlap methods and other lambdas.
// The number of subsequences created below will be the max nesting level of lambdas.
var subsequences = ImmutableArray.CreateBuilder<ImmutableArray<MethodLineExtent>.Builder>();
foreach (var extent in extentsOrderedByMinLine)
{
bool placed = false;
foreach (var subsequence in subsequences)
{
if (subsequence.Count == 0 || extent.MinLine > subsequence[subsequence.Count - 1].MaxLine)
{
subsequence.Add(extent);
placed = true;
break;
}
}
if (!placed)
{
var newRun = ImmutableArray.CreateBuilder<MethodLineExtent>();
newRun.Add(extent);
subsequences.Add(newRun);
}
}
// make all subsequences immutable:
var result = ImmutableArray.CreateBuilder<ImmutableArray<MethodLineExtent>>();
foreach (var run in subsequences)
{
result.Add(run.ToImmutable());
}
return result.ToImmutable();
}
private static IEnumerable<KeyValuePair<DocumentHandle, MethodLineExtent>> GetMethodExtents(MetadataReader reader)
{
// Perf consideration:
// We read and decode all sequence points in the file, which might be megabytes of data that need to be paged in.
// If we stored the primary document of single-document methods in a field of MethodBody table we would only need to decode
// sequence point of methods that span multiple documents to build a map from Document -> Methods.
// We can then defer decoding sequence points of methods contained in a specified document until requested.
foreach (var methodBodyHandle in reader.MethodBodies)
{
var methodBody = reader.GetMethodBody(methodBodyHandle);
var methodBodyReader = reader.GetBlobReader(methodBody.SequencePoints);
// skip signature:
methodBodyReader.ReadCompressedInteger();
// document:
var currentDocument = MetadataTokens.DocumentHandle(methodBodyReader.ReadCompressedInteger());
// sequence points:
var spReader = reader.GetSequencePointsReader(methodBody.SequencePoints);
int minLine = int.MaxValue;
int maxLine = int.MinValue;
while (spReader.MoveNext())
{
if (spReader.Current.IsHidden)
{
continue;
}
int startLine = spReader.Current.StartLine;
int endLine = spReader.Current.EndLine;
if (spReader.Current.Document != currentDocument)
{
yield return KeyValuePair.Create(currentDocument, new MethodLineExtent(methodBodyHandle, minLine, maxLine));
currentDocument = spReader.Current.Document;
minLine = startLine;
maxLine = endLine;
}
else
{
if (startLine < minLine)
{
minLine = startLine;
}
if (endLine > maxLine)
{
maxLine = endLine;
}
}
}
yield return KeyValuePair.Create(currentDocument, new MethodLineExtent(methodBodyHandle, minLine, maxLine));
}
}
public IEnumerable<MethodBodyHandle> GetMethodsContainingLine(DocumentHandle documentHandle, int line)
{
MethodsInDocument methodsInDocument;
if (!_methodsByDocument.TryGetValue(documentHandle, out methodsInDocument))
{
return null;
}
return EnumerateMethodsContainingLine(methodsInDocument.ExtentsByMinLine, line);
}
private static IEnumerable<MethodBodyHandle> EnumerateMethodsContainingLine(ImmutableArray<ImmutableArray<MethodLineExtent>> runs, int line)
{
foreach (var run in runs)
{
int index = IndexOfContainingExtent(run, line);
if (index >= 0)
{
yield return run[index].Method;
}
}
}
private static int IndexOfContainingExtent(ImmutableArray<MethodLineExtent> run, int startLine)
{
int index = run.BinarySearch(startLine, (extent, line) => extent.MinLine - line);
if (index >= 0)
{
return index;
}
int preceding = ~index - 1;
if (preceding >= 0 && startLine <= run[preceding].MaxLine)
{
return preceding;
}
return -1;
}
internal ImmutableArray<MethodLineExtent> GetMethodExtents(DocumentHandle documentHandle)
{
MethodsInDocument methodsInDocument;
if (!_methodsByDocument.TryGetValue(documentHandle, out methodsInDocument))
{
return ImmutableArray<MethodLineExtent>.Empty;
}
return methodsInDocument.ExtentsByMethod;
}
internal bool TryGetMethodSourceExtent(DocumentHandle documentHandle, MethodBodyHandle methodHandle, out int startLine, out int endLine)
{
MethodsInDocument methodsInDocument;
if (!_methodsByDocument.TryGetValue(documentHandle, out methodsInDocument))
{
startLine = endLine = 0;
return false;
}
int index = methodsInDocument.ExtentsByMethod.BinarySearch(methodHandle, (ext, handle) => HandleComparer.Default.Compare(ext.Method, handle));
if (index < 0)
{
startLine = endLine = 0;
return false;
}
var extent = methodsInDocument.ExtentsByMethod[index];
startLine = extent.MinLine;
endLine = extent.MaxLine;
return true;
}
}
}
......@@ -45,7 +45,11 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\Compilers\Core\Portable\InternalUtilities\ImmutableArrayExtensions.cs">
<Link>Utilities\ImmutableArrayExtensions.cs</Link>
</Compile>
<Compile Include="AsyncMethodData.cs" />
<Compile Include="MethodMap.cs" />
<Compile Include="Utilities\BlobWriter.cs" />
<Compile Include="Utilities\FileNameUtilities.cs" />
<Compile Include="Utilities\HResult.cs" />
......
......@@ -18,7 +18,7 @@ internal override int EndOffset
get
{
var mdReader = SymMethod.MetadataReader;
var allScopes = mdReader.GetLocalScopes(SymMethod.Handle);
var allScopes = mdReader.GetLocalScopes(SymMethod.BodyHandle);
foreach (var handle in allScopes)
{
......@@ -33,7 +33,7 @@ internal override int EndOffset
protected override ImmutableArray<ChildScopeData> CreateChildren()
{
foreach (var handle in SymMethod.MetadataReader.GetLocalScopes(SymMethod.Handle))
foreach (var handle in SymMethod.MetadataReader.GetLocalScopes(SymMethod.BodyHandle))
{
// The root scope has only a single child scope,
// which is the first scope in the scopes belonging to the method:
......
......@@ -18,18 +18,16 @@ public sealed class SymDocument : ISymUnmanagedDocument
private static Guid VendorMicrosoftGuid = new Guid("994b45c4-e6e9-11d2-903f-00c04fa302a1");
private static Guid DocumentTypeGuid = new Guid("5a869d0b-6611-11d3-bd2a-0000f80849bd");
private readonly DocumentHandle _handle;
private readonly SymReader _symReader;
internal DocumentHandle Handle { get; }
internal SymReader SymReader { get; }
internal SymDocument(SymReader symReader, DocumentHandle documentHandle)
{
Debug.Assert(symReader != null);
_symReader = symReader;
_handle = documentHandle;
SymReader = symReader;
Handle = documentHandle;
}
internal DocumentHandle Handle => _handle;
public int FindClosestLine(int line, out int closestLine)
{
// TODO:
......@@ -41,21 +39,21 @@ public int FindClosestLine(int line, out int closestLine)
out int count,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), Out]byte[] checksum)
{
var document = _symReader.MetadataReader.GetDocument(_handle);
var document = SymReader.MetadataReader.GetDocument(Handle);
if (document.Hash.IsNil)
{
count = 0;
return HResult.S_FALSE;
}
var hash = _symReader.MetadataReader.GetBlobBytes(document.Hash);
var hash = SymReader.MetadataReader.GetBlobBytes(document.Hash);
return InteropUtilities.BytesToBuffer(hash, bufferLength, out count, checksum);
}
public int GetChecksumAlgorithmId(ref Guid algorithm)
{
var document = _symReader.MetadataReader.GetDocument(_handle);
algorithm = _symReader.MetadataReader.GetGuid(document.HashAlgorithm);
var document = SymReader.MetadataReader.GetDocument(Handle);
algorithm = SymReader.MetadataReader.GetGuid(document.HashAlgorithm);
return HResult.S_OK;
}
......@@ -67,15 +65,15 @@ public int GetDocumentType(ref Guid documentType)
public int GetLanguage(ref Guid language)
{
var document = _symReader.MetadataReader.GetDocument(_handle);
language = _symReader.MetadataReader.GetGuid(document.Language);
var document = SymReader.MetadataReader.GetDocument(Handle);
language = SymReader.MetadataReader.GetGuid(document.Language);
return HResult.S_OK;
}
public int GetLanguageVendor(ref Guid vendor)
{
var document = _symReader.MetadataReader.GetDocument(_handle);
Guid languageId = _symReader.MetadataReader.GetGuid(document.Language);
var document = SymReader.MetadataReader.GetDocument(Handle);
Guid languageId = SymReader.MetadataReader.GetGuid(document.Language);
vendor = VendorMicrosoftGuid;
return HResult.S_OK;
}
......@@ -106,7 +104,7 @@ public int GetSourceLength(out int length)
out int count,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), Out]char[] url)
{
string name = _symReader.MetadataReader.GetString(_symReader.MetadataReader.GetDocument(_handle).Name);
string name = SymReader.MetadataReader.GetString(SymReader.MetadataReader.GetDocument(Handle).Name);
return InteropUtilities.StringToBuffer(name, bufferLength, out count, url);
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Metadata;
......@@ -9,24 +10,29 @@
namespace Microsoft.DiaSymReader.PortablePdb
{
[ComVisible(false)]
public sealed class SymMethod : ISymUnmanagedMethod, ISymUnmanagedAsyncMethod
public sealed class SymMethod : ISymUnmanagedMethod, ISymUnmanagedAsyncMethod, ISymEncUnmanagedMethod
{
private readonly MethodDefinitionHandle _handle;
private readonly SymReader _symReader;
internal sealed class ByHandleComparer : IComparer<ISymUnmanagedMethod>
{
public static readonly ByHandleComparer Default = new ByHandleComparer();
public int Compare(ISymUnmanagedMethod x, ISymUnmanagedMethod y) => HandleComparer.Default.Compare(((SymMethod)x).BodyHandle, ((SymMethod)y).BodyHandle);
}
internal MethodBodyHandle BodyHandle { get; }
internal MethodDefinitionHandle DefinitionHandle => BodyHandle.ToMethodDefinitionHandle();
internal SymReader SymReader { get; }
private RootScopeData _lazyRootScopeData;
private AsyncMethodData _lazyAsyncMethodData;
internal SymMethod(SymReader symReader, MethodDefinitionHandle handle)
internal MetadataReader MetadataReader => SymReader.MetadataReader;
internal SymMethod(SymReader symReader, MethodBodyHandle handle)
{
Debug.Assert(symReader != null);
_symReader = symReader;
_handle = handle;
SymReader = symReader;
BodyHandle = handle;
}
internal SymReader SymReader => _symReader;
internal MetadataReader MetadataReader => _symReader.MetadataReader;
internal MethodDefinitionHandle Handle => _handle;
#region ISymUnmanagedMethod
public int GetNamespace([MarshalAs(UnmanagedType.Interface)]out ISymUnmanagedNamespace @namespace)
......@@ -100,9 +106,9 @@ public int GetSequencePointCount(out int count)
{
// TODO: cache
var mdReader = _symReader.MetadataReader;
var mdReader = SymReader.MetadataReader;
var body = mdReader.GetMethodBody(_handle);
var body = mdReader.GetMethodBody(BodyHandle);
var spReader = mdReader.GetSequencePointsReader(body.SequencePoints);
SymDocument currentDocument = null;
......@@ -146,7 +152,7 @@ public int GetSequencePointCount(out int count)
{
if (currentDocument == null || currentDocument.Handle != sp.Document)
{
currentDocument = new SymDocument(_symReader, sp.Document);
currentDocument = new SymDocument(SymReader, sp.Document);
}
documents[i] = currentDocument;
......@@ -172,7 +178,7 @@ public int GetSequencePointCount(out int count)
public int GetToken(out int methodToken)
{
methodToken = MetadataTokens.GetToken(_handle);
methodToken = MetadataTokens.GetToken(DefinitionHandle);
return HResult.S_OK;
}
......@@ -196,7 +202,7 @@ private AsyncMethodData AsyncMethodData
private AsyncMethodData ReadAsyncMethodData()
{
var reader = MetadataReader;
var body = reader.GetMethodBody(_handle);
var body = reader.GetMethodBody(BodyHandle);
var kickoffMethod = body.GetStateMachineKickoffMethod();
if (kickoffMethod.IsNil)
......@@ -204,7 +210,7 @@ private AsyncMethodData ReadAsyncMethodData()
return AsyncMethodData.None;
}
var value = reader.GetCustomDebugInformation(_handle, MetadataUtilities.MethodSteppingInformationBlobId);
var value = reader.GetCustomDebugInformation(DefinitionHandle, MetadataUtilities.MethodSteppingInformationBlobId);
if (value.IsNil)
{
return AsyncMethodData.None;
......@@ -338,5 +344,70 @@ public int GetAsyncStepInfoCount(out int count)
}
#endregion
#region ISymEncUnmanagedMethod
/// <summary>
/// Get the file name for the line associated with speficied offset.
/// </summary>
public int GetFileNameFromOffset(
int offset,
int bufferLength,
out int count,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] char[] name)
{
// TODO: parse sequence points -> document
throw new InvalidOperationException();
}
/// <summary>
/// Get the Line information associated with <paramref name="offset"/>.
/// </summary>
/// <remarks>
/// If <paramref name="offset"/> is not a sequence point it is associated with the previous one.
/// <paramref name="sequencePointOffset"/> provides the associated sequence point.
/// </remarks>
public int GetLineFromOffset(
int offset,
out int startLine,
out int startColumn,
out int endLine,
out int endColumn,
out int sequencePointOffset)
{
// TODO: parse sequence points
throw new InvalidOperationException();
}
/// <summary>
/// Get the number of Documents that this method has lines in.
/// </summary>
public int GetDocumentsForMethodCount(out int count)
{
// TODO: parse sequence points
throw new InvalidOperationException();
}
/// <summary>
/// Get the documents this method has lines in.
/// </summary>
public int GetDocumentsForMethod(
int bufferLength,
out int count,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]ISymUnmanagedDocument[] documents)
{
// TODO: parse sequence points
throw new InvalidOperationException();
}
/// <summary>
/// Get the smallest start line and largest end line, for the method, in a specific document.
/// </summary>
public int GetSourceExtentInDocument(ISymUnmanagedDocument document, out int startLine, out int endLine)
{
return SymReader.GetMethodSourceExtentInDocument(document, this, out startLine, out endLine);
}
#endregion
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices;
......@@ -18,6 +19,7 @@ public sealed class SymReader : ISymUnmanagedReader3, ISymUnmanagedDispose
private readonly PortablePdbReader _pdbReader;
private readonly Lazy<DocumentMap> _lazyDocumentMap;
private readonly Lazy<bool> _lazyVbSemantics;
private readonly Lazy<MethodMap> _lazyMethodMap;
private int _version;
......@@ -31,6 +33,7 @@ internal SymReader(PortablePdbReader pdbReader)
_lazyDocumentMap = new Lazy<DocumentMap>(() => new DocumentMap(MetadataReader));
_lazyVbSemantics = new Lazy<bool>(() => IsVisualBasicAssembly());
_lazyMethodMap = new Lazy<MethodMap>(() => new MethodMap(MetadataReader));
}
internal MetadataReader MetadataReader => _pdbReader.MetadataReader;
......@@ -63,6 +66,22 @@ private bool IsVisualBasicAssembly()
return false;
}
private MethodMap GetMethodMap()
{
if (_pdbReader.IsDisposed)
{
throw new ObjectDisposedException(nameof(SymReader));
}
return _lazyMethodMap.Value;
}
private SymDocument AsSymDocument(ISymUnmanagedDocument document)
{
var symDocument = document as SymDocument;
return (symDocument?.SymReader == this) ? symDocument : null;
}
public int GetDocument(
[MarshalAs(UnmanagedType.LPWStr)]string url,
Guid language,
......@@ -74,6 +93,11 @@ private bool IsVisualBasicAssembly()
// SymReader: language, vendor and type parameters are ignored.
if (_pdbReader.IsDisposed)
{
throw new ObjectDisposedException("SymReader");
}
if (_lazyDocumentMap.Value.TryGetDocument(url, out documentHandle))
{
document = new SymDocument(this, documentHandle);
......@@ -151,9 +175,9 @@ public int GetMethod(int methodToken, [MarshalAs(UnmanagedType.Interface)]out IS
return HResult.E_INVALIDARG;
}
var methodDefHandle = (MethodDefinitionHandle)handle;
var methodBodyHandle = ((MethodDefinitionHandle)handle).ToMethodBodyHandle();
var methodBody = MetadataReader.GetMethodBody(methodDefHandle);
var methodBody = MetadataReader.GetMethodBody(methodBodyHandle);
if (methodBody.SequencePoints.IsNil)
{
// no debug info for the method
......@@ -161,7 +185,7 @@ public int GetMethod(int methodToken, [MarshalAs(UnmanagedType.Interface)]out IS
return HResult.E_FAIL;
}
method = new SymMethod(this, methodDefHandle);
method = new SymMethod(this, methodBodyHandle);
return HResult.S_OK;
}
......@@ -171,22 +195,146 @@ public int GetMethodByVersionPreRemap(int methodToken, int version, [MarshalAs(U
throw new NotSupportedException();
}
public int GetMethodFromDocumentPosition(ISymUnmanagedDocument document, int line, int column, [MarshalAs(UnmanagedType.Interface)]out ISymUnmanagedMethod method)
public int GetMethodFromDocumentPosition(
ISymUnmanagedDocument document,
int line,
int column,
[MarshalAs(UnmanagedType.Interface)]out ISymUnmanagedMethod method)
{
// TODO:
throw new NotImplementedException();
var symDocument = AsSymDocument(document);
if (symDocument == null)
{
method = null;
return HResult.E_INVALIDARG;
}
var methodBodyHandles = GetMethodMap().GetMethodsContainingLine(symDocument.Handle, line);
if (methodBodyHandles == null)
{
method = null;
return HResult.E_FAIL;
}
var comparer = HandleComparer.Default;
var candidate = default(MethodBodyHandle);
foreach (var methodBodyHandle in methodBodyHandles)
{
if (candidate.IsNil || comparer.Compare(methodBodyHandle, candidate) < 0)
{
candidate = methodBodyHandle;
}
}
if (candidate.IsNil)
{
method = null;
return HResult.E_FAIL;
}
method = new SymMethod(this, candidate);
return HResult.S_OK;
}
public int GetMethodsFromDocumentPosition(ISymUnmanagedDocument document, int line, int column, int bufferLength, out int count, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3), Out]ISymUnmanagedMethod[] methods)
public int GetMethodsFromDocumentPosition(
ISymUnmanagedDocument document,
int line,
int column,
int bufferLength,
out int count,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3), Out]ISymUnmanagedMethod[] methods)
{
// TODO:
throw new NotImplementedException();
var symDocument = AsSymDocument(document);
if (symDocument == null)
{
count = 0;
return HResult.E_INVALIDARG;
}
var methodBodyHandles = GetMethodMap().GetMethodsContainingLine(symDocument.Handle, line);
if (methodBodyHandles == null)
{
count = 0;
return HResult.E_FAIL;
}
if (bufferLength > 0)
{
int i = 0;
foreach (var methodBodyHandle in methodBodyHandles)
{
if (i == bufferLength)
{
break;
}
methods[i++] = new SymMethod(this, methodBodyHandle);
}
count = i;
if (i > 1)
{
Array.Sort(methods, 0, i, SymMethod.ByHandleComparer.Default);
}
}
else
{
count = methodBodyHandles.Count();
}
return HResult.S_OK;
}
public int GetMethodsInDocument(ISymUnmanagedDocument document, int bufferLength, out int count, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out]ISymUnmanagedMethod[] methods)
public int GetMethodsInDocument(
ISymUnmanagedDocument document,
int bufferLength,
out int count,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out]ISymUnmanagedMethod[] methods)
{
// TODO:
throw new NotImplementedException();
var symDocument = AsSymDocument(document);
if (symDocument == null)
{
count = 0;
return HResult.E_INVALIDARG;
}
var extentsByMethod = GetMethodMap().GetMethodExtents(symDocument.Handle);
if (bufferLength > 0)
{
int actualCount = Math.Min(extentsByMethod.Length, bufferLength);
for (int i = 0; i < actualCount; i++)
{
methods[i] = new SymMethod(this, extentsByMethod[i].Method);
}
count = actualCount;
}
else
{
count = extentsByMethod.Length;
}
count = 0;
return HResult.S_OK;
}
internal int GetMethodSourceExtentInDocument(ISymUnmanagedDocument document, SymMethod method, out int startLine, out int endLine)
{
var symDocument = AsSymDocument(document);
if (symDocument == null)
{
startLine = endLine = 0;
return HResult.E_INVALIDARG;
}
var map = GetMethodMap();
if (!map.TryGetMethodSourceExtent(symDocument.Handle, method.BodyHandle, out startLine, out endLine))
{
startLine = endLine = 0;
return HResult.E_FAIL;
}
return HResult.S_OK;
}
public int GetMethodVersion(ISymUnmanagedMethod method, out int version)
......
......@@ -89,7 +89,7 @@ public int GetAddressKind(out int kind)
out int count,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), Out]byte[] signature)
{
var localSignatureHandle = _symMethod.MetadataReader.GetMethodBody(_symMethod.Handle).LocalSignature;
var localSignatureHandle = _symMethod.MetadataReader.GetMethodBody(_symMethod.BodyHandle).LocalSignature;
var metadataImport = _symMethod.SymReader.PdbReader.MetadataImport;
var local = _symMethod.MetadataReader.GetLocalVariable(_handle);
......
using System;
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
......
......@@ -38,6 +38,8 @@ internal static int GetTypeDefOrRefOrSpecCodedIndex(EntityHandle typeHandle)
return (MetadataTokens.GetRowNumber(typeHandle) << 2) | tag;
}
// TODO: move to SRM
internal static BlobHandle GetCustomDebugInformation(this MetadataReader reader, EntityHandle parent, Guid kind)
{
foreach (var cdiHandle in reader.GetCustomDebugInformation(parent))
......@@ -52,5 +54,15 @@ internal static BlobHandle GetCustomDebugInformation(this MetadataReader reader,
return default(BlobHandle);
}
internal static MethodBodyHandle ToMethodBodyHandle(this MethodDefinitionHandle handle)
{
return (MethodBodyHandle)MetadataTokens.Handle(((int)TableIndex.MethodBody << 24) | MetadataTokens.GetRowNumber(handle));
}
internal static MethodDefinitionHandle ToMethodDefinitionHandle(this MethodBodyHandle handle)
{
return MetadataTokens.MethodDefinitionHandle(MetadataTokens.GetRowNumber(handle));
}
}
}
\ No newline at end of file
......@@ -42,6 +42,106 @@ private void Dispose(bool disposing)
Dispose(false);
}
[PreserveSig]
public unsafe int GetSigFromToken(
int tkSignature, // Signature token.
out byte* ppvSig, // return pointer to signature blob
out int pcbSig) // return size of signature
{
if (_metadataReaderOpt == null)
{
throw new NotSupportedException("Metadata not available");
}
var sig = _metadataReaderOpt.GetStandaloneSignature((StandaloneSignatureHandle)MetadataTokens.Handle(tkSignature));
var signature = _metadataReaderOpt.GetBlobBytes(sig.Signature);
GCHandle pinnedBuffer = GCHandle.Alloc(signature, GCHandleType.Pinned);
ppvSig = (byte*)pinnedBuffer.AddrOfPinnedObject();
pcbSig = signature.Length;
_pinnedBuffers.Add(pinnedBuffer);
return 0;
}
public void GetTypeDefProps(
int typeDefinition,
[MarshalAs(UnmanagedType.LPWStr), Out]StringBuilder qualifiedName,
int qualifiedNameBufferLength,
out int qualifiedNameLength,
[MarshalAs(UnmanagedType.U4)]out TypeAttributes attributes,
out int baseType)
{
if (_metadataReaderOpt == null)
{
throw new NotSupportedException("Metadata not available");
}
var handle = (TypeDefinitionHandle)MetadataTokens.Handle(typeDefinition);
var typeDef = _metadataReaderOpt.GetTypeDefinition(handle);
if (qualifiedName != null)
{
qualifiedName.Clear();
if (!typeDef.Namespace.IsNil)
{
qualifiedName.Append(_metadataReaderOpt.GetString(typeDef.Namespace));
qualifiedName.Append('.');
}
qualifiedName.Append(_metadataReaderOpt.GetString(typeDef.Name));
qualifiedNameLength = qualifiedName.Length;
}
else
{
qualifiedNameLength =
(typeDef.Namespace.IsNil ? 0 : _metadataReaderOpt.GetString(typeDef.Namespace).Length + 1) +
_metadataReaderOpt.GetString(typeDef.Name).Length;
}
baseType = MetadataTokens.GetToken(typeDef.BaseType);
attributes = typeDef.Attributes;
}
public void GetTypeRefProps(
int typeReference,
out int resolutionScope,
[MarshalAs(UnmanagedType.LPWStr), Out]StringBuilder qualifiedName,
int qualifiedNameBufferLength,
out int qualifiedNameLength)
{
if (_metadataReaderOpt == null)
{
throw new NotSupportedException("Metadata not available");
}
var handle = (TypeReferenceHandle)MetadataTokens.Handle(typeReference);
var typeRef = _metadataReaderOpt.GetTypeReference(handle);
if (qualifiedName != null)
{
qualifiedName.Clear();
if (!typeRef.Namespace.IsNil)
{
qualifiedName.Append(_metadataReaderOpt.GetString(typeRef.Namespace));
qualifiedName.Append('.');
}
qualifiedName.Append(_metadataReaderOpt.GetString(typeRef.Name));
qualifiedNameLength = qualifiedName.Length;
}
else
{
qualifiedNameLength =
(typeRef.Namespace.IsNil ? 0 : _metadataReaderOpt.GetString(typeRef.Namespace).Length + 1) +
_metadataReaderOpt.GetString(typeRef.Name).Length;
}
resolutionScope = MetadataTokens.GetToken(typeRef.ResolutionScope);
}
#region Not Implemented
public void CloseEnum(uint handleEnum)
......@@ -309,110 +409,6 @@ public Guid GetScopeProps(StringBuilder stringName, uint cchName, out uint pchNa
throw new NotImplementedException();
}
#endregion
[PreserveSig]
public unsafe int GetSigFromToken(
int tkSignature, // Signature token.
out byte* ppvSig, // return pointer to signature blob
out int pcbSig) // return size of signature
{
if (_metadataReaderOpt == null)
{
throw new NotSupportedException("Metadata not available");
}
var sig = _metadataReaderOpt.GetStandaloneSignature((StandaloneSignatureHandle)MetadataTokens.Handle(tkSignature));
var signature = _metadataReaderOpt.GetBlobBytes(sig.Signature);
GCHandle pinnedBuffer = GCHandle.Alloc(signature, GCHandleType.Pinned);
ppvSig = (byte*)pinnedBuffer.AddrOfPinnedObject();
pcbSig = signature.Length;
_pinnedBuffers.Add(pinnedBuffer);
return 0;
}
public void GetTypeDefProps(
int typeDefinition,
[MarshalAs(UnmanagedType.LPWStr), Out]StringBuilder qualifiedName,
int qualifiedNameBufferLength,
out int qualifiedNameLength,
[MarshalAs(UnmanagedType.U4)]out TypeAttributes attributes,
out int baseType)
{
if (_metadataReaderOpt == null)
{
throw new NotSupportedException("Metadata not available");
}
var handle = (TypeDefinitionHandle)MetadataTokens.Handle(typeDefinition);
var typeDef = _metadataReaderOpt.GetTypeDefinition(handle);
if (qualifiedName != null)
{
qualifiedName.Clear();
if (!typeDef.Namespace.IsNil)
{
qualifiedName.Append(_metadataReaderOpt.GetString(typeDef.Namespace));
qualifiedName.Append('.');
}
qualifiedName.Append(_metadataReaderOpt.GetString(typeDef.Name));
qualifiedNameLength = qualifiedName.Length;
}
else
{
qualifiedNameLength =
(typeDef.Namespace.IsNil ? 0 : _metadataReaderOpt.GetString(typeDef.Namespace).Length + 1) +
_metadataReaderOpt.GetString(typeDef.Name).Length;
}
baseType = MetadataTokens.GetToken(typeDef.BaseType);
attributes = typeDef.Attributes;
}
public void GetTypeRefProps(
int typeReference,
out int resolutionScope,
[MarshalAs(UnmanagedType.LPWStr), Out]StringBuilder qualifiedName,
int qualifiedNameBufferLength,
out int qualifiedNameLength)
{
if (_metadataReaderOpt == null)
{
throw new NotSupportedException("Metadata not available");
}
var handle = (TypeReferenceHandle)MetadataTokens.Handle(typeReference);
var typeRef = _metadataReaderOpt.GetTypeReference(handle);
if (qualifiedName != null)
{
qualifiedName.Clear();
if (!typeRef.Namespace.IsNil)
{
qualifiedName.Append(_metadataReaderOpt.GetString(typeRef.Namespace));
qualifiedName.Append('.');
}
qualifiedName.Append(_metadataReaderOpt.GetString(typeRef.Name));
qualifiedNameLength = qualifiedName.Length;
}
else
{
qualifiedNameLength =
(typeRef.Namespace.IsNil ? 0 : _metadataReaderOpt.GetString(typeRef.Namespace).Length + 1) +
_metadataReaderOpt.GetString(typeRef.Name).Length;
}
resolutionScope = MetadataTokens.GetToken(typeRef.ResolutionScope);
}
#region Not implemented
public unsafe uint GetTypeSpecFromToken(uint typespec, out byte* ppvSig)
{
throw new NotImplementedException();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册