|
// 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.Diagnostics;
using System.Text;
using Internal.IL;
using Internal.Text;
using Internal.TypeSystem;
using CombinedDependencyList = System.Collections.Generic.List<ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.CombinedDependencyListEntry>;
namespace ILCompiler.DependencyAnalysis
{
[DebuggerTypeProxy(typeof(MethodCodeNodeDebugView))]
public class MethodCodeNode : ObjectNode, IMethodBodyNode, INodeWithCodeInfo, INodeWithDebugInfo, ISpecialUnboxThunkNode, IMethodCodeNodeWithTypeSignature
{
private MethodDesc _method;
private ObjectData _methodCode;
private FrameInfo[] _frameInfos;
private byte[] _gcInfo;
private MethodExceptionHandlingInfoNode _ehInfo;
private DebugLocInfo[] _debugLocInfos;
private DebugVarInfo[] _debugVarInfos;
private DebugEHClauseInfo[] _debugEHClauseInfos;
private DependencyList _nonRelocationDependencies;
private MethodDebugInformation _debugInfo;
private TypeDesc[] _localTypes;
public MethodCodeNode(MethodDesc method)
{
Debug.Assert(!method.IsAbstract);
Debug.Assert(!method.IsGenericMethodDefinition && !method.OwningType.IsGenericDefinition);
Debug.Assert(method.GetCanonMethodTarget(CanonicalFormKind.Specific) == method);
_method = method;
}
public void SetCode(ObjectData data)
{
Debug.Assert(_methodCode == null);
_methodCode = data;
}
public MethodDesc Method => _method;
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
public override ObjectNodeSection GetSection(NodeFactory factory)
{
return factory.Target.IsWindows ?
ObjectNodeSection.ManagedCodeWindowsContentSection : ObjectNodeSection.ManagedCodeUnixContentSection;
}
public override bool StaticDependenciesAreComputed => _methodCode != null;
public override bool InterestingForDynamicDependencyAnalysis => _method.HasInstantiation || _method.OwningType.HasInstantiation;
public virtual void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
sb.Append(nameMangler.GetMangledMethodName(_method));
}
public int Offset => 0;
public override bool IsShareable => _method is InstantiatedMethod || EETypeNode.IsTypeNodeShareable(_method.OwningType);
public override bool HasConditionalStaticDependencies => CodeBasedDependencyAlgorithm.HasConditionalDependenciesDueToMethodCodePresence(_method);
public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory factory)
{
CombinedDependencyList dependencies = null;
CodeBasedDependencyAlgorithm.AddConditionalDependenciesDueToMethodCodePresence(ref dependencies, factory, _method);
return dependencies ?? (IEnumerable<CombinedDependencyListEntry>)Array.Empty<CombinedDependencyListEntry>();
}
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
{
DependencyList dependencies = _nonRelocationDependencies != null ? new DependencyList(_nonRelocationDependencies) : null;
TypeDesc owningType = _method.OwningType;
if (factory.PreinitializationManager.HasEagerStaticConstructor(owningType))
{
dependencies ??= new DependencyList();
dependencies.Add(factory.EagerCctorIndirection(owningType.GetStaticConstructor()), "Eager .cctor");
}
if (_ehInfo != null)
{
dependencies ??= new DependencyList();
dependencies.Add(_ehInfo, "Exception handling information");
}
if (MethodAssociatedDataNode.MethodHasAssociatedData(this))
{
dependencies ??= new DependencyList();
dependencies.Add(new DependencyListEntry(factory.MethodAssociatedData(this), "Method associated data"));
}
return dependencies;
}
public override ObjectData GetData(NodeFactory factory, bool relocsOnly)
{
return _methodCode;
}
public bool IsSpecialUnboxingThunk => ((CompilerTypeSystemContext)Method.Context).IsSpecialUnboxingThunk(_method);
public ISymbolNode GetUnboxingThunkTarget(NodeFactory factory)
{
Debug.Assert(IsSpecialUnboxingThunk);
MethodDesc nonUnboxingMethod = ((CompilerTypeSystemContext)Method.Context).GetTargetOfSpecialUnboxingThunk(_method);
return factory.MethodEntrypoint(nonUnboxingMethod, false);
}
public FrameInfo[] FrameInfos => _frameInfos;
public byte[] GCInfo => _gcInfo;
public MethodExceptionHandlingInfoNode EHInfo => _ehInfo;
public ISymbolNode GetAssociatedDataNode(NodeFactory factory)
{
if (MethodAssociatedDataNode.MethodHasAssociatedData(this))
return factory.MethodAssociatedData(this);
return null;
}
public void InitializeFrameInfos(FrameInfo[] frameInfos)
{
Debug.Assert(_frameInfos == null);
_frameInfos = frameInfos;
}
public void InitializeGCInfo(byte[] gcInfo)
{
Debug.Assert(_gcInfo == null);
_gcInfo = gcInfo;
}
public void InitializeEHInfo(ObjectData ehInfo)
{
Debug.Assert(_ehInfo == null);
if (ehInfo != null)
_ehInfo = new MethodExceptionHandlingInfoNode(_method, ehInfo);
}
public DebugLocInfo[] DebugLocInfos => _debugLocInfos;
public DebugVarInfo[] DebugVarInfos => _debugVarInfos;
public DebugEHClauseInfo[] DebugEHClauseInfos => _debugEHClauseInfos;
public bool IsStateMachineMoveNextMethod => _debugInfo.IsStateMachineMoveNextMethod;
public void InitializeDebugLocInfos(DebugLocInfo[] debugLocInfos)
{
Debug.Assert(_debugLocInfos == null);
_debugLocInfos = debugLocInfos;
}
public void InitializeDebugVarInfos(DebugVarInfo[] debugVarInfos)
{
Debug.Assert(_debugVarInfos == null);
_debugVarInfos = debugVarInfos;
}
public void InitializeDebugInfo(MethodDebugInformation debugInfo)
{
Debug.Assert(_debugInfo == null);
_debugInfo = debugInfo;
}
public void InitializeLocalTypes(TypeDesc[] localTypes)
{
Debug.Assert(_localTypes == null);
_localTypes = localTypes;
}
public void InitializeDebugEHClauseInfos(DebugEHClauseInfo[] debugEHClauseInfos)
{
Debug.Assert(_debugEHClauseInfos == null);
_debugEHClauseInfos = debugEHClauseInfos;
}
public IEnumerable<DebugVarInfoMetadata> GetDebugVars()
{
MethodSignature sig = _method.Signature;
int offset = sig.IsStatic ? 0 : 1;
var parameterNames = new string[sig.Length + offset];
int i = 0;
foreach (var paramName in _debugInfo.GetParameterNames())
{
parameterNames[i] = paramName;
i++;
}
string[] localNames;
if (_localTypes.Length > 0)
{
localNames = new string[_localTypes.Length];
var localVariables = _debugInfo.GetLocalVariables();
if (localVariables != null)
{
foreach (var local in localVariables)
{
if (!local.CompilerGenerated && local.Slot < localNames.Length)
localNames[local.Slot] = local.Name;
}
}
}
else
{
localNames = Array.Empty<string>();
}
foreach (var varInfo in _debugVarInfos)
{
if (varInfo.VarNumber < parameterNames.Length)
{
// This is a parameter
TypeDesc varType;
if (!sig.IsStatic && varInfo.VarNumber == 0)
{
varType = _method.OwningType.IsValueType ?
_method.OwningType.MakeByRefType() :
_method.OwningType;
}
else
{
varType = _method.Signature[(int)varInfo.VarNumber - offset];
}
string name = parameterNames[varInfo.VarNumber];
if (name == null)
continue;
yield return new DebugVarInfoMetadata(name, varType, isParameter: true, varInfo);
}
else
{
// This is a local
int localNumber = (int)varInfo.VarNumber - sig.Length - offset;
string name = localNames[localNumber];
if (name == null)
continue;
yield return new DebugVarInfoMetadata(name, _localTypes[localNumber], isParameter: false, varInfo);
}
}
}
public void InitializeNonRelocationDependencies(DependencyList dependencies)
{
_nonRelocationDependencies = dependencies;
}
public IEnumerable<NativeSequencePoint> GetNativeSequencePoints()
{
// This can be null if we're not generating debug info
if (_debugLocInfos == null)
yield break;
var sequencePoints = new (string Document, int LineNumber)[_debugLocInfos.Length * 4 /* chosen empirically */];
try
{
foreach (var sequencePoint in _debugInfo.GetSequencePoints())
{
int offset = sequencePoint.Offset;
if (offset >= sequencePoints.Length)
{
int newLength = Math.Max(2 * sequencePoints.Length, sequencePoint.Offset + 1);
Array.Resize(ref sequencePoints, newLength);
}
sequencePoints[offset] = (sequencePoint.Document, sequencePoint.LineNumber);
}
// Propagate last known document/line number forward to enable correct mapping when IL offsets decrease at higher native offsets
for (int i = 1; i < sequencePoints.Length; i++)
{
if (sequencePoints[i].Document == null && sequencePoints[i - 1].Document != null)
{
sequencePoints[i] = (sequencePoints[i - 1].Document, sequencePoints[i - 1].LineNumber);
}
}
}
catch (BadImageFormatException)
{
// Roslyn had a bug where it was generating bad sequence points:
// https://github.com/dotnet/roslyn/issues/20118
// Do not crash the compiler.
yield break;
}
int previousNativeOffset = -1;
string previousDocument = null;
int previousLineNumber = -1;
// OffsetMapping is sorted in order of increasing native offset (but not necessarily by IL offset)
foreach (var nativeMapping in _debugLocInfos)
{
if (nativeMapping.NativeOffset == previousNativeOffset)
continue;
var sequencePoint = sequencePoints[Math.Min(nativeMapping.ILOffset, sequencePoints.Length - 1)];
// Emit sequence point if its line number or document differ from the previous one.
// See WalkILOffsetsCallback in src/coreclr/vm/debugdebugger.cpp for more details.
if (sequencePoint.Document != null && (sequencePoint.Document != previousDocument || sequencePoint.LineNumber != previousLineNumber))
{
yield return new NativeSequencePoint(
nativeMapping.NativeOffset,
sequencePoint.Document,
sequencePoint.LineNumber);
previousNativeOffset = nativeMapping.NativeOffset;
previousDocument = sequencePoint.Document;
previousLineNumber = sequencePoint.LineNumber;
}
}
}
public override int ClassCode => 788492407;
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
{
return comparer.Compare(_method, ((MethodCodeNode)other)._method);
}
public override string ToString()
{
return _method.ToString();
}
internal sealed class MethodCodeNodeDebugView
{
private readonly MethodCodeNode _node;
public MethodCodeNodeDebugView(MethodCodeNode node)
{
_node = node;
}
public MethodDesc Method => _node.Method;
public string Disassembly
{
get
{
var sb = new StringBuilder();
sb.Append("// ");
sb.AppendLine(_node.Method.ToString());
if (_node.StaticDependenciesAreComputed)
{
var d = Disassembler.Disassemble(
_node.Method.Context.Target.Architecture,
_node._methodCode.Data,
_node._methodCode.Relocs);
sb.Append(d);
}
else
{
sb.Append("// Not compiled yet.");
}
return sb.ToString();
}
}
}
}
public readonly struct DebugLocInfo
{
public readonly int NativeOffset;
public readonly int ILOffset;
public DebugLocInfo(int nativeOffset, int ilOffset)
=> (NativeOffset, ILOffset) = (nativeOffset, ilOffset);
}
}
|