|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using Internal.IL;
using Internal.TypeSystem;
namespace ILCompiler
{
/// <summary>
/// Debug information provider that wraps another provider and generates IL assembly listing
/// (and maps debug information to it) for all methods the wrapped provider doesn't provide
/// debug information for.
/// </summary>
public class ILAssemblyGeneratingMethodDebugInfoProvider : DebugInformationProvider, IDisposable
{
private readonly DebugInformationProvider _wrappedProvider;
private readonly string _fileName;
private readonly StreamWriter _tw;
private readonly Dictionary<MethodDesc, MethodDebugInformation> _generatedInfos = new Dictionary<MethodDesc, MethodDebugInformation>();
private int _currentLine;
public ILAssemblyGeneratingMethodDebugInfoProvider(string fileName, DebugInformationProvider wrappedProvider = null)
{
_wrappedProvider = wrappedProvider;
_fileName = fileName;
_tw = new StreamWriter(new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None));
}
public override MethodDebugInformation GetDebugInfo(MethodIL methodIL)
{
MethodDebugInformation debugInfo = _wrappedProvider?.GetDebugInfo(methodIL);
if (debugInfo != null && debugInfo != MethodDebugInformation.None)
return debugInfo;
MethodIL definitionIL = methodIL.GetMethodILDefinition();
// One file per compilation model has obvious stability problems in multithreaded
// compilation. This would be fixable by e.g. generating multiple files, or preparing
// the file after the IL scanning phase. Since this is a non-shipping feature,
// we're going to ignore the stability concern here.
lock (_generatedInfos)
{
if (!_generatedInfos.TryGetValue(definitionIL.OwningMethod, out debugInfo))
{
debugInfo = GetDebugInformation(definitionIL);
_generatedInfos.Add(definitionIL.OwningMethod, debugInfo);
}
return debugInfo;
}
}
private SyntheticMethodDebugInformation GetDebugInformation(MethodIL methodIL)
{
MethodDesc owningMethod = methodIL.OwningMethod;
var disasm = new ILDisassembler(methodIL);
var fmt = new ILDisassembler.ILTypeNameFormatter(null);
ArrayBuilder<ILSequencePoint> sequencePoints = default(ArrayBuilder<ILSequencePoint>);
_tw.Write(".method ");
// TODO: accessibility, specialname, calling conventions etc.
if (!owningMethod.Signature.IsStatic)
_tw.Write("instance ");
_tw.Write(fmt.FormatName(owningMethod.Signature.ReturnType));
_tw.Write(" ");
_tw.Write(owningMethod.GetName());
if (owningMethod.HasInstantiation)
{
_tw.Write("<");
for (int i = 0; i < owningMethod.Instantiation.Length; i++)
{
if (i != 0)
_tw.Write(", ");
_tw.Write(fmt.FormatName(owningMethod.Instantiation[i]));
}
_tw.Write(">");
}
_tw.Write("(");
for (int i = 0; i < owningMethod.Signature.Length; i++)
{
if (i != 0)
_tw.Write(", ");
_tw.Write(fmt.FormatName(owningMethod.Signature[i]));
}
_tw.WriteLine(") cil managed"); _currentLine++;
_tw.WriteLine("{"); _currentLine++;
_tw.Write(" // Code size: ");
_tw.Write(disasm.CodeSize);
_tw.WriteLine(); _currentLine++;
_tw.Write(" .maxstack ");
_tw.Write(methodIL.MaxStack);
_tw.WriteLine(); _currentLine++;
LocalVariableDefinition[] locals = methodIL.GetLocals();
if (locals != null && locals.Length > 0)
{
_tw.Write(" .locals ");
if (methodIL.IsInitLocals)
_tw.Write("init ");
_tw.Write("(");
for (int i = 0; i < locals.Length; i++)
{
if (i != 0)
{
_tw.WriteLine(","); _currentLine++;
_tw.Write(" ");
}
_tw.Write(fmt.FormatName(locals[i].Type));
_tw.Write(" ");
if (locals[i].IsPinned)
_tw.Write("pinned ");
_tw.Write("V_");
_tw.Write(i);
}
_tw.WriteLine(")"); _currentLine++;
}
_tw.WriteLine(); _currentLine++;
while (disasm.HasNextInstruction)
{
_currentLine++;
int offset = disasm.Offset;
_tw.WriteLine(disasm.GetNextInstruction());
sequencePoints.Add(new ILSequencePoint(offset, _fileName, _currentLine));
}
_tw.WriteLine("}"); _currentLine++;
_tw.WriteLine(); _currentLine++;
return new SyntheticMethodDebugInformation(sequencePoints.ToArray());
}
public void Dispose()
{
_tw.Dispose();
}
private sealed class SyntheticMethodDebugInformation : MethodDebugInformation
{
private readonly ILSequencePoint[] _sequencePoints;
public SyntheticMethodDebugInformation(ILSequencePoint[] sequencePoints)
{
_sequencePoints = sequencePoints;
}
public override IEnumerable<ILSequencePoint> GetSequencePoints()
{
return _sequencePoints;
}
}
}
}
|