|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
extern alias DSR;
using System.Collections.Immutable;
using System.Linq;
using DSR::Microsoft.DiaSymReader;
using Microsoft.CodeAnalysis.Debugging;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
using Xunit;
namespace Roslyn.Test.Utilities
{
internal sealed class MethodDebugInfoBytes
{
public readonly ImmutableArray<byte> Bytes;
public readonly ISymUnmanagedMethod Method;
public MethodDebugInfoBytes(ImmutableArray<byte> bytes, ISymUnmanagedMethod method)
{
this.Bytes = bytes;
this.Method = method;
}
/// <remarks>
/// This is a helper class for creating mostly-correct <see cref="MethodDebugInfoBytes"/> objects (e.g. circular forwards, extra records, etc).
/// To create totally broken objects (e.g. corrupted bytes, alternate scope structures, etc), construct <see cref="MethodDebugInfoBytes"/> objects directly.
/// </remarks>
internal sealed class Builder
{
private const byte Version = 4;
private const byte Padding = 0;
private readonly ISymUnmanagedMethod _method;
private ArrayBuilder<byte> _bytesBuilder;
private int _recordCount;
public Builder(string[][] importStringGroups = null, bool suppressUsingInfo = false, ISymUnmanagedConstant[] constants = null)
{
_bytesBuilder = ArrayBuilder<byte>.GetInstance();
if (importStringGroups != null && !suppressUsingInfo)
{
var groupSizes = importStringGroups.Select(g => (short)g.Length).ToArray();
AddUsingInfo(groupSizes);
}
var namespaces = importStringGroups == null
? default(ImmutableArray<ISymUnmanagedNamespace>)
: importStringGroups.SelectMany(names => names.Select(name => (ISymUnmanagedNamespace)new MockSymUnmanagedNamespace(name))).ToImmutableArray();
var childScope = new MockSymUnmanagedScope(default(ImmutableArray<ISymUnmanagedScope>), namespaces, constants);
var rootScope = new MockSymUnmanagedScope(ImmutableArray.Create<ISymUnmanagedScope>(childScope), default(ImmutableArray<ISymUnmanagedNamespace>));
_method = new MockSymUnmanagedMethod(rootScope);
}
public Builder AddUsingInfo(params short[] groupSizes)
{
var numGroupSizes = groupSizes.Length;
var recordSize = BitArithmeticUtilities.Align(4 + 4 + 2 + 2 * numGroupSizes, 4); // Record size, including header.
// Record header
_bytesBuilder.Add(Version);
_bytesBuilder.Add((byte)CustomDebugInfoKind.UsingGroups);
_bytesBuilder.Add(Padding);
_bytesBuilder.Add(Padding);
_bytesBuilder.Add4(recordSize);
// Record body
_bytesBuilder.Add2((short)numGroupSizes);
foreach (var groupSize in groupSizes)
{
_bytesBuilder.Add2(groupSize);
}
if ((_bytesBuilder.Count % 4) != 0)
{
_bytesBuilder.Add2(0);
}
Assert.Equal(0, _bytesBuilder.Count % 4);
_recordCount++;
return this;
}
public Builder AddForward(int targetToken)
{
return AddForward(targetToken, isModuleLevel: false);
}
public Builder AddModuleForward(int targetToken)
{
return AddForward(targetToken, isModuleLevel: true);
}
private Builder AddForward(int targetToken, bool isModuleLevel)
{
// Record header
_bytesBuilder.Add(Version);
_bytesBuilder.Add((byte)(isModuleLevel ? CustomDebugInfoKind.ForwardModuleInfo : CustomDebugInfoKind.ForwardMethodInfo));
_bytesBuilder.Add(Padding);
_bytesBuilder.Add(Padding);
_bytesBuilder.Add4(12); // Record size, including header.
// Record body
_bytesBuilder.Add4(targetToken);
Assert.Equal(0, _bytesBuilder.Count % 4);
_recordCount++;
return this;
}
public MethodDebugInfoBytes Build()
{
// Global header
_bytesBuilder.Insert(0, Version);
_bytesBuilder.Insert(1, (byte)_recordCount);
_bytesBuilder.Insert(2, Padding);
_bytesBuilder.Insert(3, Padding);
Assert.Equal(0, _bytesBuilder.Count % 4);
var info = new MethodDebugInfoBytes(_bytesBuilder.ToImmutableAndFree(), _method);
_bytesBuilder = null; // We'll blow up if any other methods are called.
return info;
}
}
}
}
|