When we need to generate a textual stack trace in NativeAOT, we rely on two things - reflection metadata for methods, or a supplementary stack trace metadata format.
The reflection metadata case is simple - reflection keeps track of method addresses and the associated metadata like names and signatures. We ask for metadata handles (tokens, basically) and we get the handles back. We then walk the metadata and format textual strings.
When reflection metadata is not available, we have to emit some other metadata in the compiler. We use the same metadata format and conceptually the closest thing were unresolvable `MemberReference`s, so we basically generated a pair of method pointer + MemberReference handle for each method body that doesn't have reflection metadata. It's a lot less metadata than for a resolvable definition, but is still quite a bit wasteful.
This change avoids generating full `MemberReference` and instead inlines owning type/name/signature handle/token into the mapping table. We also do it in a way that avoids emitting owning type/name/signature if it's the same as the record before that.
_typeContext = typeContext;
}
- public static string FormatMethodName(MetadataReader metadataReader, Handle methodHandle)
+ public static string FormatMethodName(MetadataReader metadataReader, Handle owningType, ConstantStringValueHandle name, MethodSignatureHandle signature, ConstantStringArrayHandle genericArguments)
{
- MethodNameFormatter formatter = new MethodNameFormatter(metadataReader, SigTypeContext.FromMethod(metadataReader, methodHandle));
- formatter.EmitMethodName(methodHandle);
+ MethodNameFormatter formatter = new MethodNameFormatter(metadataReader, SigTypeContext.FromMethod(metadataReader, owningType, genericArguments));
+ formatter.EmitTypeName(owningType, namespaceQualified: true);
+ formatter._outputBuilder.Append('.');
+ formatter.EmitString(name);
+
+ if (!genericArguments.IsNull(metadataReader))
+ {
+ var args = metadataReader.GetConstantStringArray(genericArguments);
+ bool first = true;
+ foreach (Handle handle in args.Value)
+ {
+ if (first)
+ {
+ first = false;
+ formatter._outputBuilder.Append('[');
+ }
+ else
+ {
+ formatter._outputBuilder.Append(',');
+ }
+ formatter.EmitString(handle.ToConstantStringValueHandle(metadataReader));
+ }
+ if (!first)
+ {
+ formatter._outputBuilder.Append(']');
+ }
+ }
+
+ formatter.EmitMethodParameters(metadataReader.GetMethodSignature(signature));
+
return formatter._outputBuilder.ToString();
}
}
/// <summary>
- /// Emit a given method signature to a specified string builder.
- /// </summary>
- /// <param name="methodHandle">Method reference or instantiation token</param>
- private void EmitMethodName(Handle methodHandle)
- {
- switch (methodHandle.HandleType)
- {
- case HandleType.MemberReference:
- EmitMethodReferenceName(methodHandle.ToMemberReferenceHandle(_metadataReader));
- break;
-
- case HandleType.MethodInstantiation:
- EmitMethodInstantiationName(methodHandle.ToMethodInstantiationHandle(_metadataReader));
- break;
-
- case HandleType.QualifiedMethod:
- EmitMethodDefinitionName(methodHandle.ToQualifiedMethodHandle(_metadataReader));
- break;
-
- default:
- Debug.Assert(false);
- _outputBuilder.Append("???");
- break;
- }
- }
-
- /// <summary>
- /// Emit method reference to the output string builder.
- /// </summary>
- /// <param name="memberRefHandle">Member reference handle</param>
- private void EmitMethodReferenceName(MemberReferenceHandle memberRefHandle)
- {
- MemberReference methodRef = _metadataReader.GetMemberReference(memberRefHandle);
- MethodSignature methodSignature;
- EmitContainingTypeAndMethodName(methodRef, out methodSignature);
- EmitMethodParameters(methodSignature);
- }
-
- /// <summary>
- /// Emit generic method instantiation to the output string builder.
- /// </summary>
- /// <param name="methodInstHandle">Method instantiation handle</param>
- private void EmitMethodInstantiationName(MethodInstantiationHandle methodInstHandle)
- {
- MethodInstantiation methodInst = _metadataReader.GetMethodInstantiation(methodInstHandle);
-
- if (methodInst.Method.HandleType == HandleType.MemberReference)
- {
- MemberReferenceHandle methodRefHandle = methodInst.Method.ToMemberReferenceHandle(_metadataReader);
- MemberReference methodRef = methodRefHandle.GetMemberReference(_metadataReader);
- EmitContainingTypeAndMethodName(methodRef, out MethodSignature methodSignature);
- EmitGenericArguments(methodInst.GenericTypeArguments);
- EmitMethodParameters(methodSignature);
- }
- else
- {
- QualifiedMethodHandle qualifiedMethodHandle = methodInst.Method.ToQualifiedMethodHandle(_metadataReader);
- QualifiedMethod qualifiedMethod = _metadataReader.GetQualifiedMethod(qualifiedMethodHandle);
- EmitContainingTypeAndMethodName(qualifiedMethod);
- EmitGenericArguments(methodInst.GenericTypeArguments);
- EmitMethodParameters(qualifiedMethod.Method);
- }
- }
-
- private void EmitMethodDefinitionName(QualifiedMethodHandle qualifiedMethodHandle)
- {
- QualifiedMethod qualifiedMethod = _metadataReader.GetQualifiedMethod(qualifiedMethodHandle);
- EmitContainingTypeAndMethodName(qualifiedMethod);
- EmitMethodParameters(qualifiedMethod.Method);
- }
-
- /// <summary>
- /// Emit containing type and method name and extract the method signature from a method reference.
- /// </summary>
- /// <param name="methodRef">Method reference to format</param>
- /// <param name="methodSignature">Output method signature</param>
- private void EmitContainingTypeAndMethodName(MemberReference methodRef, out MethodSignature methodSignature)
- {
- methodSignature = _metadataReader.GetMethodSignature(methodRef.Signature.ToMethodSignatureHandle(_metadataReader));
- EmitTypeName(methodRef.Parent, namespaceQualified: true);
- _outputBuilder.Append('.');
- EmitString(methodRef.Name);
- }
-
- private void EmitContainingTypeAndMethodName(QualifiedMethod qualifiedMethod)
- {
- Method method = _metadataReader.GetMethod(qualifiedMethod.Method);
- EmitTypeName(qualifiedMethod.EnclosingType, namespaceQualified: true);
- _outputBuilder.Append('.');
- EmitString(method.Name);
- }
-
- /// <summary>
/// Emit parenthesized method argument type list.
/// </summary>
/// <param name="methodSignature">Method signature to use for parameter formatting</param>
EmitFunctionPointerTypeName();
break;
+ // This is not an actual type, but we don't always bother representing generic arguments on generic methods as types
+ case HandleType.ConstantStringValue:
+ EmitString(typeHandle.ToConstantStringValueHandle(_metadataReader));
+ break;
+
default:
Debug.Assert(false, $"Type handle {typeHandle.HandleType} was not handled");
_outputBuilder.Append("???");
}
}
- public static SigTypeContext FromMethod(MetadataReader metadataReader, Handle methodHandle)
+ public static SigTypeContext FromMethod(MetadataReader metadataReader, Handle enclosingTypeHandle, ConstantStringArrayHandle methodInst)
{
- object typeContext;
- object methodContext;
-
- switch (methodHandle.HandleType)
- {
- case HandleType.MemberReference:
- typeContext = GetTypeContext(metadataReader, methodHandle);
- methodContext = default(HandleCollection);
- break;
-
- case HandleType.MethodInstantiation:
- MethodInstantiation methodInst = methodHandle.ToMethodInstantiationHandle(metadataReader).GetMethodInstantiation(metadataReader);
- typeContext = GetTypeContext(metadataReader, methodInst.Method);
- methodContext = methodInst.GenericTypeArguments;
- break;
-
- case HandleType.QualifiedMethod:
- QualifiedMethod qualifiedMethod = methodHandle.ToQualifiedMethodHandle(metadataReader).GetQualifiedMethod(metadataReader);
- typeContext = GetTypeContext(metadataReader, qualifiedMethod.EnclosingType);
- methodContext = qualifiedMethod.Method.GetMethod(metadataReader).GenericParameters;
- break;
- default:
- Debug.Assert(false);
- return default(SigTypeContext);
- }
-
- return new SigTypeContext(typeContext, methodContext);
+ object methodContext = null;
+ if (!methodInst.IsNull(metadataReader))
+ methodContext = methodInst.GetConstantStringArray(metadataReader).Value;
+ return new SigTypeContext(GetTypeContext(metadataReader, enclosingTypeHandle), methodContext);
}
public static SigTypeContext FromMethod(MetadataReader metadataReader, TypeDefinitionHandle enclosingTypeHandle, MethodHandle methodHandle)
using System.Collections.Generic;
using Internal.Metadata.NativeFormat;
+using Internal.NativeFormat;
using Internal.Runtime;
using Internal.Runtime.Augments;
using Internal.Runtime.TypeLoader;
using Internal.TypeSystem;
using ReflectionExecution = Internal.Reflection.Execution.ReflectionExecution;
+using Debug = System.Diagnostics.Debug;
namespace Internal.StackTraceMetadata
{
/// <summary>
/// Dictionary mapping method RVA's to tokens within the metadata blob.
/// </summary>
- private readonly Dictionary<int, int> _methodRvaToTokenMap;
+ private readonly StackTraceData[] _stacktraceDatas;
/// <summary>
/// Metadata reader for the stack trace metadata.
{
_metadataReader = new MetadataReader(new IntPtr(metadataBlob), (int)metadataBlobSize);
- // RVA to token map consists of pairs of integers (method RVA - token)
- int rvaToTokenMapEntryCount = (int)(rvaToTokenMapBlobSize / (2 * sizeof(int)));
- _methodRvaToTokenMap = new Dictionary<int, int>(rvaToTokenMapEntryCount);
- PopulateRvaToTokenMap(handle, (int *)rvaToTokenMapBlob, rvaToTokenMapEntryCount);
+ int entryCount = *(int*)rvaToTokenMapBlob;
+ _stacktraceDatas = new StackTraceData[entryCount];
+
+ PopulateRvaToTokenMap(handle, rvaToTokenMapBlob + sizeof(int), rvaToTokenMapBlobSize - sizeof(int));
}
}
/// within a single binary module.
/// </summary>
/// <param name="handle">Module to use to construct the mapping</param>
- /// <param name="rvaToTokenMap">List of RVA - token pairs</param>
- /// <param name="entryCount">Number of the RVA - token pairs in the list</param>
- private unsafe void PopulateRvaToTokenMap(TypeManagerHandle handle, int *rvaToTokenMap, int entryCount)
+ /// <param name="pMap">List of RVA - token pairs</param>
+ /// <param name="length">Length of the blob</param>
+ private unsafe void PopulateRvaToTokenMap(TypeManagerHandle handle, byte* pMap, uint length)
{
- for (int entryIndex = 0; entryIndex < entryCount; entryIndex++)
+ Handle currentOwningType = default;
+ MethodSignatureHandle currentSignature = default;
+ ConstantStringValueHandle currentName = default;
+ ConstantStringArrayHandle currentMethodInst = default;
+
+ int current = 0;
+ byte* pCurrent = pMap;
+ while (pCurrent < pMap + length)
{
- int* pRelPtr32 = &rvaToTokenMap[2 * entryIndex + 0];
- byte* pointer = (byte*)pRelPtr32 + *pRelPtr32;
- int methodRva = (int)(pointer - (byte*)handle.OsModuleBase);
- int token = rvaToTokenMap[2 * entryIndex + 1];
- _methodRvaToTokenMap[methodRva] = token;
+ byte command = *pCurrent++;
+
+ if ((command & StackTraceDataCommand.UpdateOwningType) != 0)
+ {
+ currentOwningType = Handle.FromIntToken((int)NativePrimitiveDecoder.ReadUInt32(ref pCurrent));
+ Debug.Assert(currentOwningType.HandleType is HandleType.TypeDefinition or HandleType.TypeReference or HandleType.TypeSpecification);
+ }
+
+ if ((command & StackTraceDataCommand.UpdateName) != 0)
+ {
+ currentName = new Handle(HandleType.ConstantStringValue, (int)NativePrimitiveDecoder.DecodeUnsigned(ref pCurrent)).ToConstantStringValueHandle(_metadataReader);
+ }
+
+ if ((command & StackTraceDataCommand.UpdateSignature) != 0)
+ {
+ currentSignature = new Handle(HandleType.MethodSignature, (int)NativePrimitiveDecoder.DecodeUnsigned(ref pCurrent)).ToMethodSignatureHandle(_metadataReader);
+ currentMethodInst = default;
+ }
+
+ if ((command & StackTraceDataCommand.UpdateGenericSignature) != 0)
+ {
+ currentSignature = new Handle(HandleType.MethodSignature, (int)NativePrimitiveDecoder.DecodeUnsigned(ref pCurrent)).ToMethodSignatureHandle(_metadataReader);
+ currentMethodInst = new Handle(HandleType.ConstantStringArray, (int)NativePrimitiveDecoder.DecodeUnsigned(ref pCurrent)).ToConstantStringArrayHandle(_metadataReader);
+ }
+
+ void* pMethod = ReadRelPtr32(pCurrent);
+ pCurrent += sizeof(int);
+
+ Debug.Assert((nint)pMethod > handle.OsModuleBase);
+ int methodRva = (int)((nint)pMethod - handle.OsModuleBase);
+
+ _stacktraceDatas[current++] = new StackTraceData
+ {
+ Rva = methodRva,
+ OwningType = currentOwningType,
+ Name = currentName,
+ Signature = currentSignature,
+ GenericArguments = currentMethodInst,
+ };
+
+ static void* ReadRelPtr32(byte* address)
+ => address + *(int*)address;
}
+
+ Debug.Assert(current == _stacktraceDatas.Length);
+
+ Array.Sort(_stacktraceDatas);
}
/// <summary>
/// </summary>
public string GetMethodNameFromRvaIfAvailable(int rva)
{
- if (_methodRvaToTokenMap == null)
+ if (_stacktraceDatas == null)
{
// No stack trace metadata for this module
return null;
}
- int rawToken;
- if (!_methodRvaToTokenMap.TryGetValue(rva, out rawToken))
+ int index = Array.BinarySearch(_stacktraceDatas, new StackTraceData() { Rva = rva });
+ if (index < 0)
{
// Method RVA not found in the map
return null;
}
- return MethodNameFormatter.FormatMethodName(_metadataReader, Handle.FromIntToken(rawToken));
+ StackTraceData data = _stacktraceDatas[index];
+ return MethodNameFormatter.FormatMethodName(_metadataReader, data.OwningType, data.Name, data.Signature, data.GenericArguments);
+ }
+
+ private struct StackTraceData : IComparable<StackTraceData>
+ {
+ public int Rva { get; init; }
+ public Handle OwningType { get; init; }
+ public ConstantStringValueHandle Name { get; init; }
+ public MethodSignatureHandle Signature { get; init; }
+ public ConstantStringArrayHandle GenericArguments { get; init; }
+
+ public int CompareTo(StackTraceData other) => Rva.CompareTo(other.Rva);
}
}
}
<Compile Include="Internal\Runtime\CompilerHelpers\LibraryInitializer.cs" />
<Compile Include="Internal\StackTraceMetadata\StackTraceMetadata.cs" />
<Compile Include="Internal\StackTraceMetadata\MethodNameFormatter.cs" />
+ <Compile Include="$(CompilerCommonPath)\Internal\NativeFormat\NativeFormatReader.Primitives.cs">
+ <Link>Internal\NativeFormat\NativeFormatReader.Primitives.cs</Link>
+ </Compile>
<Compile Include="$(CompilerCommonPath)\Internal\Runtime\MetadataBlob.cs">
<Link>Internal\Runtime\MetadataBlob.cs</Link>
</Compile>
+ <Compile Include="$(CompilerCommonPath)\Internal\Runtime\StackTraceData.cs">
+ <Link>Internal\Runtime\StackTraceData.cs</Link>
+ </Compile>
</ItemGroup>
</Project>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Internal.Runtime
+{
+ internal static class StackTraceDataCommand
+ {
+ public const byte UpdateOwningType = 0x01;
+ public const byte UpdateName = 0x02;
+ public const byte UpdateSignature = 0x04;
+ public const byte UpdateGenericSignature = 0x08; // Just a shortcut - sig metadata has the info
+ }
+}
out List<MetadataMapping<MetadataType>> typeMappings,
out List<MetadataMapping<MethodDesc>> methodMappings,
out List<MetadataMapping<FieldDesc>> fieldMappings,
- out List<MetadataMapping<MethodDesc>> stackTraceMapping)
+ out List<StackTraceMapping> stackTraceMapping)
{
ComputeMetadata(new Policy(_blockingPolicy, this), factory,
out metadataBlob,
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Collections.Generic;
+using Internal.Runtime;
using Internal.Text;
+using Internal.TypeSystem;
+
+using Debug = System.Diagnostics.Debug;
namespace ILCompiler.DependencyAnalysis
{
objData.RequireInitialPointerAlignment();
objData.AddSymbol(this);
- foreach (var mappingEntry in factory.MetadataManager.GetStackTraceMapping(factory))
+ var mapping = new List<StackTraceMapping>(factory.MetadataManager.GetStackTraceMapping(factory));
+
+ // The information is encoded as a set of commands: set current owning type, set current method name, etc.
+ // Sort things so that we don't thrash the current entity too much.
+ mapping.Sort((x, y) =>
+ {
+ // Group methods on the same generic type definition together
+ int result = x.OwningTypeHandle.CompareTo(y.OwningTypeHandle);
+ if (result != 0)
+ return result;
+
+ // Overloads get grouped together too
+ result = x.MethodNameHandle.CompareTo(y.MethodNameHandle);
+ if (result != 0)
+ return result;
+
+ // Methods that only differ in something generic get grouped too
+ result = x.MethodSignatureHandle.CompareTo(y.MethodSignatureHandle);
+ if (result != 0)
+ return result;
+
+ // At this point the genericness should be the same
+ Debug.Assert(x.MethodInstantiationArgumentCollectionHandle == y.MethodInstantiationArgumentCollectionHandle);
+
+ // Compare by the method as a tie breaker to get stable sort
+ return TypeSystemComparer.Instance.Compare(x.Method, y.Method);
+ });
+
+ int currentOwningType = 0;
+ int currentSignature = 0;
+ int currentName = 0;
+
+ // The first int contains the number of entries
+ objData.EmitInt(mapping.Count);
+
+ foreach (var entry in mapping)
{
- objData.EmitReloc(factory.MethodEntrypoint(mappingEntry.Entity), RelocType.IMAGE_REL_BASED_RELPTR32);
- objData.EmitInt(mappingEntry.MetadataHandle);
+ var commandReservation = objData.ReserveByte();
+
+ byte command = 0;
+ if (currentOwningType != entry.OwningTypeHandle)
+ {
+ currentOwningType = entry.OwningTypeHandle;
+ command |= StackTraceDataCommand.UpdateOwningType;
+ objData.EmitInt(currentOwningType);
+ }
+
+ if (currentName != entry.MethodNameHandle)
+ {
+ currentName = entry.MethodNameHandle;
+ command |= StackTraceDataCommand.UpdateName;
+ objData.EmitCompressedUInt((uint)(currentName & MetadataManager.MetadataOffsetMask));
+ }
+
+ if (currentSignature != entry.MethodSignatureHandle)
+ {
+ currentSignature = entry.MethodSignatureHandle;
+ objData.EmitCompressedUInt((uint)(currentSignature & MetadataManager.MetadataOffsetMask));
+
+ if (entry.MethodInstantiationArgumentCollectionHandle != 0)
+ {
+ command |= StackTraceDataCommand.UpdateGenericSignature;
+ objData.EmitCompressedUInt((uint)(entry.MethodInstantiationArgumentCollectionHandle & MetadataManager.MetadataOffsetMask));
+ }
+ else
+ {
+ command |= StackTraceDataCommand.UpdateSignature;
+ }
+ }
+ objData.EmitByte(commandReservation, command);
+ objData.EmitReloc(factory.MethodEntrypoint(entry.Method), RelocType.IMAGE_REL_BASED_RELPTR32);
}
_size = objData.CountBytes;
out List<MetadataMapping<MetadataType>> typeMappings,
out List<MetadataMapping<MethodDesc>> methodMappings,
out List<MetadataMapping<FieldDesc>> fieldMappings,
- out List<MetadataMapping<MethodDesc>> stackTraceMapping) where TPolicy : struct, IMetadataPolicy
+ out List<StackTraceMapping> stackTraceMapping) where TPolicy : struct, IMetadataPolicy
{
var transformed = MetadataTransform.Run(policy, GetCompilationModulesWithMetadata());
MetadataTransform transform = transformed.Transform;
writer.ScopeDefinitions.AddRange(transformed.Scopes);
// Generate entries in the blob for methods that will be necessary for stack trace purposes.
- var stackTraceRecords = new List<KeyValuePair<MethodDesc, MetadataRecord>>();
+ var stackTraceRecords = new List<StackTraceRecordData>();
foreach (var methodBody in GetCompiledMethodBodies())
{
MethodDesc method = methodBody.Method;
if (!_stackTraceEmissionPolicy.ShouldIncludeMethod(method))
continue;
- MetadataRecord record = CreateStackTraceRecord(transform, method);
+ StackTraceRecordData record = CreateStackTraceRecord(transform, method);
- stackTraceRecords.Add(new KeyValuePair<MethodDesc, MetadataRecord>(
- method,
- record));
+ stackTraceRecords.Add(record);
- writer.AdditionalRootRecords.Add(record);
+ writer.AdditionalRootRecords.Add(record.OwningType);
+ writer.AdditionalRootRecords.Add(record.MethodName);
+ writer.AdditionalRootRecords.Add(record.MethodSignature);
+ writer.AdditionalRootRecords.Add(record.MethodInstantiationArgumentCollection);
}
var ms = new MemoryStream();
typeMappings = new List<MetadataMapping<MetadataType>>();
methodMappings = new List<MetadataMapping<MethodDesc>>();
fieldMappings = new List<MetadataMapping<FieldDesc>>();
- stackTraceMapping = new List<MetadataMapping<MethodDesc>>();
+ stackTraceMapping = new List<StackTraceMapping>();
// Generate type definition mappings
foreach (var type in factory.MetadataManager.GetTypesWithEETypes())
// Generate stack trace metadata mapping
foreach (var stackTraceRecord in stackTraceRecords)
{
- stackTraceMapping.Add(new MetadataMapping<MethodDesc>(stackTraceRecord.Key, writer.GetRecordHandle(stackTraceRecord.Value)));
+ StackTraceMapping mapping = new StackTraceMapping(
+ stackTraceRecord.Method,
+ writer.GetRecordHandle(stackTraceRecord.OwningType),
+ writer.GetRecordHandle(stackTraceRecord.MethodSignature),
+ writer.GetRecordHandle(stackTraceRecord.MethodName),
+ stackTraceRecord.MethodInstantiationArgumentCollection != null ? writer.GetRecordHandle(stackTraceRecord.MethodInstantiationArgumentCollection) : 0);
+ stackTraceMapping.Add(mapping);
}
}
using CustomAttributeValue = System.Reflection.Metadata.CustomAttributeValue<Internal.TypeSystem.TypeDesc>;
using MetadataRecord = Internal.Metadata.NativeFormat.Writer.MetadataRecord;
-using MemberReference = Internal.Metadata.NativeFormat.Writer.MemberReference;
using TypeReference = Internal.Metadata.NativeFormat.Writer.TypeReference;
using TypeSpecification = Internal.Metadata.NativeFormat.Writer.TypeSpecification;
using ConstantStringValue = Internal.Metadata.NativeFormat.Writer.ConstantStringValue;
using TypeInstantiationSignature = Internal.Metadata.NativeFormat.Writer.TypeInstantiationSignature;
-using MethodInstantiation = Internal.Metadata.NativeFormat.Writer.MethodInstantiation;
+using ConstantStringArray = Internal.Metadata.NativeFormat.Writer.ConstantStringArray;
namespace ILCompiler
{
private List<MetadataMapping<MetadataType>> _typeMappings;
private List<MetadataMapping<FieldDesc>> _fieldMappings;
private List<MetadataMapping<MethodDesc>> _methodMappings;
- private List<MetadataMapping<MethodDesc>> _stackTraceMappings;
+ private List<StackTraceMapping> _stackTraceMappings;
protected readonly CompilerTypeSystemContext _typeSystemContext;
protected readonly MetadataBlockingPolicy _blockingPolicy;
out List<MetadataMapping<MetadataType>> typeMappings,
out List<MetadataMapping<MethodDesc>> methodMappings,
out List<MetadataMapping<FieldDesc>> fieldMappings,
- out List<MetadataMapping<MethodDesc>> stackTraceMapping);
+ out List<StackTraceMapping> stackTraceMapping);
- protected MetadataRecord CreateStackTraceRecord(Metadata.MetadataTransform transform, MethodDesc method)
+ protected StackTraceRecordData CreateStackTraceRecord(Metadata.MetadataTransform transform, MethodDesc method)
{
// In the metadata, we only represent the generic definition
MethodDesc methodToGenerateMetadataFor = method.GetTypicalMethodDefinition();
- MetadataRecord record = transform.HandleQualifiedMethod(methodToGenerateMetadataFor);
- // If we're generating a MemberReference to a method on a generic type, the owning type
+ ConstantStringValue name = (ConstantStringValue)methodToGenerateMetadataFor.Name;
+ MetadataRecord signature = transform.HandleMethodSignature(methodToGenerateMetadataFor.Signature);
+ MetadataRecord owningType = transform.HandleType(methodToGenerateMetadataFor.OwningType);
+
+ // If we're generating record for a method on a generic type, the owning type
// should appear as if instantiated over its formals
TypeDesc owningTypeToGenerateMetadataFor = methodToGenerateMetadataFor.OwningType;
if (owningTypeToGenerateMetadataFor.HasInstantiation
- && record is MemberReference memberRefRecord
- && memberRefRecord.Parent is TypeReference)
+ && owningType is TypeReference)
{
List<MetadataRecord> genericArgs = new List<MetadataRecord>();
foreach (Internal.TypeSystem.Ecma.EcmaGenericParameter genericParam in owningTypeToGenerateMetadataFor.Instantiation)
});
}
- memberRefRecord.Parent = new TypeSpecification
+ owningType = new TypeSpecification
{
Signature = new TypeInstantiationSignature
{
- GenericType = memberRefRecord.Parent,
+ GenericType = owningType,
GenericTypeArguments = genericArgs,
}
};
}
- // As a twist, instantiated generic methods appear as if instantiated over their formals.
+ // Generate metadata for the method instantiation arguments
+ ConstantStringArray methodInst;
if (methodToGenerateMetadataFor.HasInstantiation)
{
- var methodInst = new MethodInstantiation
- {
- Method = record,
- };
- methodInst.GenericTypeArguments.Capacity = methodToGenerateMetadataFor.Instantiation.Length;
+ methodInst = new ConstantStringArray();
foreach (Internal.TypeSystem.Ecma.EcmaGenericParameter typeArgument in methodToGenerateMetadataFor.Instantiation)
{
- var genericParam = new TypeReference
- {
- TypeName = (ConstantStringValue)typeArgument.Name,
- };
- methodInst.GenericTypeArguments.Add(genericParam);
+ methodInst.Value.Add((ConstantStringValue)typeArgument.Name);
}
- record = methodInst;
+ }
+ else
+ {
+ methodInst = null;
}
- return record;
+ return new StackTraceRecordData(method, owningType, signature, name, methodInst);
}
/// <summary>
return _fieldMappings;
}
- public IEnumerable<MetadataMapping<MethodDesc>> GetStackTraceMapping(NodeFactory factory)
+ public IEnumerable<StackTraceMapping> GetStackTraceMapping(NodeFactory factory)
{
EnsureMetadataGenerated(factory);
return _stackTraceMappings;
}
}
- public struct MetadataMapping<TEntity>
+ public readonly struct MetadataMapping<TEntity>
{
public readonly TEntity Entity;
public readonly int MetadataHandle;
public MetadataMapping(TEntity entity, int metadataHandle)
- {
- Entity = entity;
- MetadataHandle = metadataHandle;
- }
+ => (Entity, MetadataHandle) = (entity, metadataHandle);
+ }
+
+ public readonly struct StackTraceMapping
+ {
+ public readonly MethodDesc Method;
+ public readonly int OwningTypeHandle;
+ public readonly int MethodSignatureHandle;
+ public readonly int MethodNameHandle;
+ public readonly int MethodInstantiationArgumentCollectionHandle;
+
+ public StackTraceMapping(MethodDesc method, int owningTypeHandle, int methodSignatureHandle, int methodNameHandle, int methodInstantiationArgumentCollectionHandle)
+ => (Method, OwningTypeHandle, MethodSignatureHandle, MethodNameHandle, MethodInstantiationArgumentCollectionHandle)
+ = (method, owningTypeHandle, methodSignatureHandle, methodNameHandle, methodInstantiationArgumentCollectionHandle);
+ }
+
+ public readonly struct StackTraceRecordData
+ {
+ public readonly MethodDesc Method;
+ public readonly MetadataRecord OwningType;
+ public readonly MetadataRecord MethodSignature;
+ public readonly MetadataRecord MethodName;
+ public readonly MetadataRecord MethodInstantiationArgumentCollection;
+
+ public StackTraceRecordData(MethodDesc method, MetadataRecord owningType, MetadataRecord methodSignature, MetadataRecord methodName, MetadataRecord methodInstantiationArgumentCollection)
+ => (Method, OwningType, MethodSignature, MethodName, MethodInstantiationArgumentCollection)
+ = (method, owningType, methodSignature, methodName, methodInstantiationArgumentCollection);
}
[Flags]
out List<MetadataMapping<MetadataType>> typeMappings,
out List<MetadataMapping<MethodDesc>> methodMappings,
out List<MetadataMapping<FieldDesc>> fieldMappings,
- out List<MetadataMapping<MethodDesc>> stackTraceMapping)
+ out List<StackTraceMapping> stackTraceMapping)
{
ComputeMetadata(new GeneratedTypesAndCodeMetadataPolicy(_blockingPolicy, factory),
factory, out metadataBlob, out typeMappings, out methodMappings, out fieldMappings, out stackTraceMapping);
<Compile Include="..\..\Common\Internal\Runtime\RuntimeConstants.cs">
<Link>Common\RuntimeConstants.cs</Link>
</Compile>
+ <Compile Include="..\..\Common\Internal\Runtime\StackTraceData.cs">
+ <Link>Common\StackTraceData.cs</Link>
+ </Compile>
<Compile Include="..\..\Common\Internal\Runtime\UniversalGenericParameterLayout.cs">
<Link>Common\UniversalGenericParameterLayout.cs</Link>
</Compile>