dotnet-installer/src/Microsoft.Extensions.Testing.Abstractions/SymUnmanagedReaderExtensions.cs

116 lines
4.4 KiB
C#

// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using Microsoft.DiaSymReader;
using System.Linq;
namespace Microsoft.Extensions.Testing.Abstractions
{
internal static class SymUnmanagedReaderExtensions
{
internal const int E_FAIL = unchecked((int)0x80004005);
internal const int E_NOTIMPL = unchecked((int)0x80004001);
private static readonly IntPtr s_ignoreIErrorInfo = new IntPtr(-1);
private delegate int ItemsGetter<TEntity, TItem>(TEntity entity, int bufferLength, out int count, TItem[] buffer);
internal static void ThrowExceptionForHR(int hr)
{
// E_FAIL indicates "no info".
// E_NOTIMPL indicates a lack of ISymUnmanagedReader support (in a particular implementation).
if (hr < 0 && hr != E_FAIL && hr != E_NOTIMPL)
{
Marshal.ThrowExceptionForHR(hr, s_ignoreIErrorInfo);
}
}
internal static SourceInformation GetSourceInformation(this ISymUnmanagedMethod method)
{
var sequencePoint = method.GetSequencePoints().OrderBy(s => s.StartLine).FirstOrDefault();
var fileName = sequencePoint.Document.GetName();
var lineNumber = sequencePoint.StartLine;
return new SourceInformation(fileName, lineNumber);
}
internal static ImmutableArray<SymUnmanagedSequencePoint> GetSequencePoints(this ISymUnmanagedMethod method)
{
// NB: method.GetSequencePoints(0, out numAvailable, ...) always returns 0.
int numAvailable;
int hr = method.GetSequencePointCount(out numAvailable);
SymUnmanagedReaderExtensions.ThrowExceptionForHR(hr);
if (numAvailable == 0)
{
return ImmutableArray<SymUnmanagedSequencePoint>.Empty;
}
int[] offsets = new int[numAvailable];
ISymUnmanagedDocument[] documents = new ISymUnmanagedDocument[numAvailable];
int[] startLines = new int[numAvailable];
int[] startColumns = new int[numAvailable];
int[] endLines = new int[numAvailable];
int[] endColumns = new int[numAvailable];
int numRead;
hr = method.GetSequencePoints(numAvailable, out numRead, offsets, documents, startLines, startColumns, endLines, endColumns);
SymUnmanagedReaderExtensions.ThrowExceptionForHR(hr);
if (numRead != numAvailable)
{
throw new InvalidOperationException($"Read only {numRead} of {numAvailable} sequence points.");
}
var builder = ImmutableArray.CreateBuilder<SymUnmanagedSequencePoint>(numRead);
for (int i = 0; i < numRead; i++)
{
builder.Add(new SymUnmanagedSequencePoint(
offsets[i],
documents[i],
startLines[i],
startColumns[i],
endLines[i],
endColumns[i]));
}
return builder.ToImmutable();
}
internal static string GetName(this ISymUnmanagedDocument document)
{
return ToString(GetItems(document,
(ISymUnmanagedDocument a, int b, out int c, char[] d) => a.GetUrl(b, out c, d)));
}
private static TItem[] GetItems<TEntity, TItem>(TEntity entity, ItemsGetter<TEntity, TItem> getter)
{
int count;
int hr = getter(entity, 0, out count, null);
ThrowExceptionForHR(hr);
if (count == 0)
{
return null;
}
var result = new TItem[count];
hr = getter(entity, count, out count, result);
ThrowExceptionForHR(hr);
ValidateItems(count, result.Length);
return result;
}
private static void ValidateItems(int actualCount, int bufferLength)
{
if (actualCount != bufferLength)
{
throw new InvalidOperationException(string.Format("Read only {0} of {1} items.", actualCount, bufferLength));
}
}
private static string ToString(char[] buffer)
{
return new string(buffer, 0, buffer.Length - 1);
}
}
}