|
// 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.Reflection.Metadata;
using Internal.Text;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
using Internal.TypeSystem.TypesDebugInfo;
using ILCompiler.DependencyAnalysis;
namespace ILCompiler
{
public class UserDefinedTypeDescriptor
{
private object _lock = new object();
private NodeFactory _nodeFactory;
private NodeFactory NodeFactory => _nodeFactory;
private bool Is64Bit => NodeFactory.Target.PointerSize == 8;
private TargetAbi Abi => NodeFactory.Target.Abi;
public UserDefinedTypeDescriptor(ITypesDebugInfoWriter objectWriter, NodeFactory nodeFactory)
{
_objectWriter = objectWriter;
_nodeFactory = nodeFactory;
}
// Get type index for use as a variable/parameter
public uint GetVariableTypeIndex(TypeDesc type)
{
lock (_lock)
{
return GetVariableTypeIndex(type, true);
}
}
public uint GetStateMachineThisVariableTypeIndex(TypeDesc type)
{
// If the state machine is a valuetype, the this parameter will be a byref
if (type.IsByRef)
{
type = type.GetParameterType();
Debug.Assert(type.IsValueType);
}
type = DebuggerCanonicalize(type);
lock (_lock)
{
if (!_knownStateMachineThisTypes.TryGetValue(type, out uint typeIndex))
{
Debug.Assert(type.IsDefType);
MetadataType defType = (MetadataType)type;
ClassTypeDescriptor classTypeDescriptor = new ClassTypeDescriptor
{
IsStruct = 1,
Name = new Utf8String($"StateMachineLocals_{System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(((EcmaType)defType.GetTypeDefinition()).Handle):X}"),
InstanceSize = defType.InstanceByteCount.IsIndeterminate ? 0 : (ulong)defType.InstanceByteCount.AsInt,
};
var fieldsDescs = default(ArrayBuilder<DataFieldDescriptor>);
foreach (var fieldDesc in defType.GetFields())
{
if (fieldDesc.IsStatic)
continue;
// We're going to parse the Roslyn-generated backing fields and unmangle them back to usable names.
// We'll also skip some infrastructural fields that are not interesting (like the field that
// holds the initial value of parameters, the current state, the current thread ID, etc.).
// The set of fields we're going to touch is tagged as "parsed by the expression evaluator"
// in the Roslyn codebase, so it's somewhat safe to do this.
// https://github.com/dotnet/roslyn/blob/afd10305a37c0ffb2cfb2c2d8446154c68cfa87a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs#L15-L22
string fieldNameEmit = fieldDesc.GetName();
if (fieldNameEmit.Length > 0 && fieldNameEmit[0] == '<')
{
if (TryGetGeneratedNameKind(fieldNameEmit, out char kind))
{
if (kind == '4' /* ThisProxy */)
{
fieldNameEmit = "this";
}
else if (kind == '5' /* HoistedLocalField */)
{
fieldNameEmit = fieldNameEmit.Substring(1, fieldNameEmit.IndexOf('>') - 1);
}
else
{
// The rest of fields support the state machine infrastructure
continue;
}
}
static bool TryGetGeneratedNameKind(string name, out char kind)
{
int endIndex = name.IndexOf('>');
if (endIndex > 0 && endIndex + 1 < name.Length)
{
kind = name[endIndex + 1];
return true;
}
kind = default;
return false;
}
}
LayoutInt fieldOffset = fieldDesc.Offset;
int fieldOffsetEmit = fieldOffset.IsIndeterminate ? 0xBAAD : fieldOffset.AsInt;
TypeDesc fieldType = GetFieldDebugType(fieldDesc);
uint fieldTypeIndex = GetVariableTypeIndex(fieldType, false);
DataFieldDescriptor field = new DataFieldDescriptor
{
FieldTypeIndex = fieldTypeIndex,
Offset = (ulong)fieldOffsetEmit,
Name = new Utf8String(fieldNameEmit)
};
fieldsDescs.Add(field);
}
LayoutInt elementSize = defType.GetElementSize();
int elementSizeEmit = elementSize.IsIndeterminate ? 0xBAAD : elementSize.AsInt;
ClassFieldsTypeDescriptor fieldsDescriptor = new ClassFieldsTypeDescriptor
{
Size = (ulong)elementSizeEmit,
FieldsCount = fieldsDescs.Count,
};
uint completeTypeIndex = _objectWriter.GetCompleteClassTypeIndex(classTypeDescriptor, fieldsDescriptor, fieldsDescs.ToArray(), Array.Empty<StaticDataFieldDescriptor>());
PointerTypeDescriptor descriptor = new PointerTypeDescriptor
{
ElementType = completeTypeIndex,
Is64Bit = Is64Bit ? 1 : 0,
IsConst = 0,
IsReference = 1,
};
typeIndex = _objectWriter.GetPointerTypeIndex(descriptor);
_knownStateMachineThisTypes.Add(type, typeIndex);
}
return typeIndex;
}
}
// Get Type index for this pointer of specified type
public uint GetThisTypeIndex(TypeDesc type)
{
lock (_lock)
{
uint typeIndex;
if (_thisTypes.TryGetValue(type, out typeIndex))
return typeIndex;
PointerTypeDescriptor descriptor = default(PointerTypeDescriptor);
// Note the use of GetTypeIndex here instead of GetVariableTypeIndex (We need the type exactly, not a reference to the type (as would happen for arrays/classes), and not a primitive value (as would happen for primitives))
descriptor.ElementType = GetTypeIndex(type, true);
descriptor.Is64Bit = Is64Bit ? 1 : 0;
descriptor.IsConst = 1;
descriptor.IsReference = 0;
typeIndex = _objectWriter.GetPointerTypeIndex(descriptor);
_thisTypes.Add(type, typeIndex);
return typeIndex;
}
}
// Get type index for method
public uint GetMethodTypeIndex(MethodDesc method)
{
lock (_lock)
{
uint typeIndex;
if (_methodIndices.TryGetValue(method, out typeIndex))
return typeIndex;
MemberFunctionTypeDescriptor descriptor = default(MemberFunctionTypeDescriptor);
MethodSignature signature = method.Signature;
descriptor.ReturnType = GetVariableTypeIndex(DebuggerCanonicalize(signature.ReturnType));
descriptor.ThisAdjust = 0;
descriptor.CallingConvention = 0x4; // Near fastcall
descriptor.TypeIndexOfThisPointer = signature.IsStatic ?
GetPrimitiveTypeIndex(method.OwningType.Context.GetWellKnownType(WellKnownType.Void)) :
GetThisTypeIndex(method.OwningType);
descriptor.ContainingClass = GetTypeIndex(method.OwningType, true);
try
{
descriptor.NumberOfArguments = checked((ushort)signature.Length);
}
catch (OverflowException)
{
return 0;
}
uint[] args = new uint[signature.Length];
for (int i = 0; i < args.Length; i++)
args[i] = GetVariableTypeIndex(DebuggerCanonicalize(signature[i]));
typeIndex = _objectWriter.GetMemberFunctionTypeIndex(descriptor, args);
_methodIndices.Add(method, typeIndex);
return typeIndex;
}
}
// Get type index for specific method by name
public uint GetMethodFunctionIdTypeIndex(MethodDesc method)
{
lock (_lock)
{
uint typeIndex;
if (_methodIdIndices.TryGetValue(method, out typeIndex))
return typeIndex;
MemberFunctionIdTypeDescriptor descriptor = default(MemberFunctionIdTypeDescriptor);
descriptor.MemberFunction = GetMethodTypeIndex(method);
descriptor.ParentClass = GetTypeIndex(method.OwningType, true);
descriptor.Name = new Utf8String(method.Name.ToArray());
typeIndex = _objectWriter.GetMemberFunctionId(descriptor);
_methodIdIndices.Add(method, typeIndex);
return typeIndex;
}
}
private static TypeDesc DebuggerCanonicalize(TypeDesc type)
{
if (type.IsCanonicalSubtype(CanonicalFormKind.Any))
return type.ConvertToCanonForm(CanonicalFormKind.Specific);
return type;
}
private uint GetVariableTypeIndex(TypeDesc type, bool needsCompleteIndex)
{
uint variableTypeIndex;
if (type.IsPrimitive)
{
variableTypeIndex = GetPrimitiveTypeIndex(type);
}
else
{
type = DebuggerCanonicalize(type);
if ((type.IsDefType && !type.IsValueType) || type.IsArray)
{
// The type index of a variable/field of a reference type is wrapped
// in a pointer, as these fields are really pointer fields, and the data is on the heap
if (_knownReferenceWrappedTypes.TryGetValue(type, out variableTypeIndex))
{
return variableTypeIndex;
}
else
{
uint typeindex = GetTypeIndex(type, false);
PointerTypeDescriptor descriptor = default(PointerTypeDescriptor);
descriptor.ElementType = typeindex;
descriptor.Is64Bit = Is64Bit ? 1 : 0;
descriptor.IsConst = 0;
descriptor.IsReference = 1;
variableTypeIndex = _objectWriter.GetPointerTypeIndex(descriptor);
_knownReferenceWrappedTypes[type] = variableTypeIndex;
return variableTypeIndex;
}
}
else if (type.IsEnum)
{
// Enum's use the LF_ENUM record as the variable type index.
if (_enumTypes.TryGetValue(type, out variableTypeIndex))
return variableTypeIndex;
variableTypeIndex = GetEnumTypeIndex(type);
_enumTypes[type] = variableTypeIndex;
return variableTypeIndex;
}
variableTypeIndex = GetTypeIndex(type, needsCompleteIndex);
}
return variableTypeIndex;
}
/// <summary>
/// Get type index for type without the type being wrapped as a reference (as a variable or field must be)
/// </summary>
/// <param name="type"></param>
/// <param name="needsCompleteType"></param>
/// <returns></returns>
public uint GetTypeIndex(TypeDesc type, bool needsCompleteType)
{
uint typeIndex;
if (needsCompleteType ?
_completeKnownTypes.TryGetValue(type, out typeIndex)
: _knownTypes.TryGetValue(type, out typeIndex))
{
return typeIndex;
}
else
{
return GetNewTypeIndex(type, needsCompleteType);
}
}
private uint GetNewTypeIndex(TypeDesc type, bool needsCompleteType)
{
if (type.IsArray)
{
return GetArrayTypeIndex(type);
}
else if (type.IsDefType)
{
return GetClassTypeIndex(type, needsCompleteType);
}
else if (type.IsPointer)
{
return GetPointerTypeIndex(((ParameterizedType)type).ParameterType);
}
else if (type.IsByRef)
{
return GetByRefTypeIndex(((ParameterizedType)type).ParameterType);
}
else if (type.IsFunctionPointer)
{
return GetPointerTypeIndex(type.Context.GetWellKnownType(WellKnownType.Void));
}
Debug.Fail("Unhandled UserDefinedTypeDescriptor type: {type}");
return 0;
}
private uint GetPointerTypeIndex(TypeDesc pointeeType)
{
uint typeIndex;
if (_pointerTypes.TryGetValue(pointeeType, out typeIndex))
return typeIndex;
PointerTypeDescriptor descriptor = default(PointerTypeDescriptor);
descriptor.ElementType = GetVariableTypeIndex(pointeeType, false);
descriptor.Is64Bit = Is64Bit ? 1 : 0;
descriptor.IsConst = 0;
descriptor.IsReference = 0;
// Calling GetVariableTypeIndex may have filled in _pointerTypes
if (_pointerTypes.TryGetValue(pointeeType, out typeIndex))
return typeIndex;
typeIndex = _objectWriter.GetPointerTypeIndex(descriptor);
_pointerTypes.Add(pointeeType, typeIndex);
return typeIndex;
}
private uint GetByRefTypeIndex(TypeDesc pointeeType)
{
uint typeIndex;
if (_byRefTypes.TryGetValue(pointeeType, out typeIndex))
return typeIndex;
PointerTypeDescriptor descriptor = default(PointerTypeDescriptor);
descriptor.ElementType = GetVariableTypeIndex(pointeeType, false);
descriptor.Is64Bit = Is64Bit ? 1 : 0;
descriptor.IsConst = 0;
descriptor.IsReference = 1;
// Calling GetVariableTypeIndex may have filled in _byRefTypes
if (_byRefTypes.TryGetValue(pointeeType, out typeIndex))
return typeIndex;
typeIndex = _objectWriter.GetPointerTypeIndex(descriptor);
_byRefTypes.Add(pointeeType, typeIndex);
return typeIndex;
}
private uint GetEnumTypeIndex(TypeDesc type)
{
Debug.Assert(type.IsEnum, "GetEnumTypeIndex was called with wrong type");
DefType defType = type as DefType;
Debug.Assert(defType != null, "GetEnumTypeIndex was called with non def type");
List<FieldDesc> fieldsDescriptors = new List<FieldDesc>();
foreach (var field in defType.GetFields())
{
if (field.IsLiteral)
{
fieldsDescriptors.Add(field);
}
}
EnumTypeDescriptor enumTypeDescriptor = new EnumTypeDescriptor
{
ElementCount = (ulong)fieldsDescriptors.Count,
ElementType = GetPrimitiveTypeIndex(defType.UnderlyingType),
Name = _objectWriter.GetMangledName(type),
};
EnumRecordTypeDescriptor[] typeRecords = new EnumRecordTypeDescriptor[enumTypeDescriptor.ElementCount];
for (int i = 0; i < fieldsDescriptors.Count; ++i)
{
FieldDesc field = fieldsDescriptors[i];
EnumRecordTypeDescriptor recordTypeDescriptor;
recordTypeDescriptor.Value = GetEnumRecordValue(field);
recordTypeDescriptor.Name = new Utf8String(field.Name.ToArray());
typeRecords[i] = recordTypeDescriptor;
}
uint typeIndex = _objectWriter.GetEnumTypeIndex(enumTypeDescriptor, typeRecords);
return typeIndex;
}
private uint GetArrayTypeIndex(TypeDesc type)
{
Debug.Assert(type.IsArray, "GetArrayTypeIndex was called with wrong type");
ArrayType arrayType = (ArrayType)type;
uint elementSize = (uint)type.Context.Target.PointerSize;
LayoutInt layoutElementSize = arrayType.GetElementSize();
if (!layoutElementSize.IsIndeterminate)
elementSize = (uint)layoutElementSize.AsInt;
ArrayTypeDescriptor arrayTypeDescriptor = new ArrayTypeDescriptor
{
Rank = (uint)arrayType.Rank,
ElementType = GetVariableTypeIndex(arrayType.ElementType, false),
Size = elementSize,
IsMultiDimensional = arrayType.IsMdArray ? 1 : 0
};
ClassTypeDescriptor classDescriptor = new ClassTypeDescriptor
{
IsStruct = 0,
Name = _objectWriter.GetMangledName(type),
BaseClassId = GetTypeIndex(arrayType.BaseType, false)
};
uint typeIndex = _objectWriter.GetArrayTypeIndex(classDescriptor, arrayTypeDescriptor);
_knownTypes[type] = typeIndex;
_completeKnownTypes[type] = typeIndex;
return typeIndex;
}
private static ulong GetEnumRecordValue(FieldDesc field)
{
var ecmaField = field as EcmaField;
if (ecmaField != null)
{
MetadataReader reader = ecmaField.MetadataReader;
FieldDefinition fieldDef = reader.GetFieldDefinition(ecmaField.Handle);
ConstantHandle defaultValueHandle = fieldDef.GetDefaultValue();
if (!defaultValueHandle.IsNil)
{
return HandleConstant(ecmaField.Module, defaultValueHandle);
}
}
return 0;
}
private static ulong HandleConstant(EcmaModule module, ConstantHandle constantHandle)
{
MetadataReader reader = module.MetadataReader;
Constant constant = reader.GetConstant(constantHandle);
BlobReader blob = reader.GetBlobReader(constant.Value);
switch (constant.TypeCode)
{
case ConstantTypeCode.Byte:
return (ulong)blob.ReadByte();
case ConstantTypeCode.Int16:
return (ulong)blob.ReadInt16();
case ConstantTypeCode.Int32:
return (ulong)blob.ReadInt32();
case ConstantTypeCode.Int64:
return (ulong)blob.ReadInt64();
case ConstantTypeCode.SByte:
return (ulong)blob.ReadSByte();
case ConstantTypeCode.UInt16:
return (ulong)blob.ReadUInt16();
case ConstantTypeCode.UInt32:
return (ulong)blob.ReadUInt32();
case ConstantTypeCode.UInt64:
return (ulong)blob.ReadUInt64();
}
Debug.Assert(false);
return 0;
}
private bool ShouldUseCanonicalTypeRecord(TypeDesc type)
{
// TODO: check the type's generic complexity
return GetGenericDepth(type) > NodeFactory.TypeSystemContext.GenericsConfig.MaxGenericDepthOfDebugRecord;
static int GetGenericDepth(TypeDesc type)
{
if (type.HasInstantiation)
{
int maxGenericDepthInInstantiation = 0;
foreach (TypeDesc instantiationType in type.Instantiation)
{
maxGenericDepthInInstantiation = Math.Max(GetGenericDepth(instantiationType), maxGenericDepthInInstantiation);
}
return maxGenericDepthInInstantiation + 1;
}
if (type.IsParameterizedType)
return 1 + GetGenericDepth(((ParameterizedType)type).ParameterType);
return 0;
}
}
private TypeDesc GetDebugType(TypeDesc type)
{
// To avoid infinite generic recursion issues, attempt to use canonical form for fields with high generic complexity.
if (type.IsCanonicalSubtype(CanonicalFormKind.Specific) || ShouldUseCanonicalTypeRecord(type))
{
type = type.ConvertToCanonForm(CanonicalFormKind.Specific);
if (ShouldUseCanonicalTypeRecord(type))
{
type = type.ConvertToCanonForm(CanonicalFormKind.Universal);
}
}
return type;
}
private TypeDesc GetFieldDebugType(FieldDesc field)
{
return GetDebugType(field.FieldType);
}
private uint GetClassTypeIndex(TypeDesc type, bool needsCompleteType)
{
TypeDesc debugType = GetDebugType(type);
MetadataType defType = debugType as MetadataType;
Debug.Assert(defType != null, "GetClassTypeIndex was called with non def type");
ClassTypeDescriptor classTypeDescriptor = new ClassTypeDescriptor
{
IsStruct = type.IsValueType ? 1 : 0,
Name = _objectWriter.GetMangledName(defType),
BaseClassId = 0,
InstanceSize = 0
};
uint typeIndex = _objectWriter.GetClassTypeIndex(classTypeDescriptor);
_knownTypes[type] = typeIndex;
if (!defType.InstanceByteCount.IsIndeterminate)
{
classTypeDescriptor.InstanceSize = (ulong)defType.InstanceByteCount.AsInt;
}
if (type.HasBaseType && !type.IsValueType)
{
classTypeDescriptor.BaseClassId = GetTypeIndex(defType.BaseType, true);
}
else if (type.IsInterface)
{
// Allows debuggers to vtcast the types and see the real instance types.
classTypeDescriptor.BaseClassId = GetTypeIndex(type.Context.GetWellKnownType(WellKnownType.Object), true);
}
List<DataFieldDescriptor> fieldsDescs = new List<DataFieldDescriptor>();
List<DataFieldDescriptor> nonGcStaticFields = new List<DataFieldDescriptor>();
List<DataFieldDescriptor> gcStaticFields = new List<DataFieldDescriptor>();
List<DataFieldDescriptor> threadStaticFields = new List<DataFieldDescriptor>();
List<StaticDataFieldDescriptor> staticsDescs = new List<StaticDataFieldDescriptor>();
Utf8String nonGcStaticDataName = NodeFactory.NameMangler.NodeMangler.NonGCStatics(type);
Utf8String gcStaticDataName = NodeFactory.NameMangler.NodeMangler.GCStatics(type);
Utf8String threadStaticDataName = NodeFactory.NameMangler.NodeMangler.ThreadStatics(type);
bool isNativeAOT = Abi == TargetAbi.NativeAot;
bool hasNonGcStatics = NodeFactory.MetadataManager.HasNonGcStaticBase(defType);
bool hasGcStatics = NodeFactory.MetadataManager.HasGcStaticBase(defType);
bool hasThreadStatics = NodeFactory.MetadataManager.HasThreadStaticBase(defType);
bool hasInstanceFields = defType.IsValueType || NodeFactory.MetadataManager.HasConstructedEEType(defType);
bool isCanonical = defType.IsCanonicalSubtype(CanonicalFormKind.Any);
foreach (var fieldDesc in defType.GetFields())
{
if (fieldDesc.HasRva || fieldDesc.IsLiteral)
continue;
if (fieldDesc.IsStatic)
{
if (isCanonical)
continue;
if (fieldDesc.IsThreadStatic && !hasThreadStatics)
continue;
if (fieldDesc.HasGCStaticBase)
{
if (!hasGcStatics)
continue;
}
else
{
if (!hasNonGcStatics)
continue;
}
}
else
{
if (!hasInstanceFields)
continue;
}
LayoutInt fieldOffset = fieldDesc.Offset;
int fieldOffsetEmit = fieldOffset.IsIndeterminate ? 0xBAAD : fieldOffset.AsInt;
TypeDesc fieldType = GetFieldDebugType(fieldDesc);
// We're going to drill into this type more deeply and it might be more than what the
// compiler already looked at. e.g. if this is a reference type instance field that
// has fields that don't resolve we might hit resolution exceptions in the process:
//
// class NeverInstantiated
// {
// private UnresolvableType Foo;
// }
//
// class GeneratingDebugInfoForThis
// {
// // Going to throw here because instance layout of UnresolvableType cannot be computed.
// private NeverInstantiated Bar;
// }
//
// If this happens, just treat the field as Object or IntPtr. Better than crashing here.
//
// We are limiting this try/catch to when the type is not a valuetype. If it's a valuetype,
// we would generate a very invalid debug info. This should have been prevented elsewhere.
uint fieldTypeIndex;
try
{
fieldTypeIndex = GetVariableTypeIndex(fieldType, false);
}
catch (TypeSystemException) when (!fieldType.IsValueType)
{
fieldTypeIndex = fieldType.IsGCPointer ?
GetVariableTypeIndex(fieldType.Context.GetWellKnownType(WellKnownType.Object))
: GetVariableTypeIndex(fieldType.Context.GetWellKnownType(WellKnownType.IntPtr));
}
DataFieldDescriptor field = new DataFieldDescriptor
{
FieldTypeIndex = fieldTypeIndex,
Offset = (ulong)fieldOffsetEmit,
Name = new Utf8String(fieldDesc.Name.ToArray())
};
if (fieldDesc.IsStatic)
{
if (NodeFactory.Target.OperatingSystem != TargetOS.Windows)
{
StaticDataFieldDescriptor staticDesc = new StaticDataFieldDescriptor
{
StaticOffset = (ulong)fieldOffsetEmit
};
// Mark field as static
field.Offset = 0xFFFFFFFF;
if (fieldDesc.IsThreadStatic) {
staticDesc.StaticDataName = threadStaticDataName;
staticDesc.IsStaticDataInObject = isNativeAOT ? 1 : 0;
} else if (fieldDesc.HasGCStaticBase) {
staticDesc.StaticDataName = gcStaticDataName;
staticDesc.IsStaticDataInObject = isNativeAOT ? 1 : 0;
} else {
staticDesc.StaticDataName = nonGcStaticDataName;
staticDesc.IsStaticDataInObject = 0;
}
staticsDescs.Add(staticDesc);
}
if (fieldDesc.IsThreadStatic)
threadStaticFields.Add(field);
else if (fieldDesc.HasGCStaticBase)
gcStaticFields.Add(field);
else
nonGcStaticFields.Add(field);
}
else
{
fieldsDescs.Add(field);
}
}
if (NodeFactory.Target.OperatingSystem == TargetOS.Windows)
{
InsertStaticFieldRegionMember(fieldsDescs, defType, nonGcStaticFields, WindowsNodeMangler.NonGCStaticMemberName, false, false);
InsertStaticFieldRegionMember(fieldsDescs, defType, gcStaticFields, WindowsNodeMangler.GCStaticMemberName, isNativeAOT, false);
InsertStaticFieldRegionMember(fieldsDescs, defType, threadStaticFields, WindowsNodeMangler.ThreadStaticMemberName, isNativeAOT, true);
}
else
{
fieldsDescs.AddRange(nonGcStaticFields);
fieldsDescs.AddRange(gcStaticFields);
fieldsDescs.AddRange(threadStaticFields);
}
DataFieldDescriptor[] fields = new DataFieldDescriptor[fieldsDescs.Count];
for (int i = 0; i < fieldsDescs.Count; ++i)
{
fields[i] = fieldsDescs[i];
}
StaticDataFieldDescriptor[] statics = new StaticDataFieldDescriptor[staticsDescs.Count];
for (int i = 0; i < staticsDescs.Count; ++i)
{
statics[i] = staticsDescs[i];
}
LayoutInt instanceSize = defType.IsValueType ? defType.InstanceFieldSize : defType.InstanceByteCount;
int instanceSizeEmit = instanceSize.IsIndeterminate ? 0xBAAD : instanceSize.AsInt;
ClassFieldsTypeDescriptor fieldsDescriptor = new ClassFieldsTypeDescriptor
{
Size = (ulong)instanceSizeEmit,
FieldsCount = fieldsDescs.Count,
};
uint completeTypeIndex = _objectWriter.GetCompleteClassTypeIndex(classTypeDescriptor, fieldsDescriptor, fields, statics);
_completeKnownTypes[type] = completeTypeIndex;
if (needsCompleteType)
return completeTypeIndex;
else
return typeIndex;
}
private void InsertStaticFieldRegionMember(List<DataFieldDescriptor> fieldDescs, DefType defType, List<DataFieldDescriptor> staticFields, Utf8String staticFieldForm,
bool staticDataInObject, bool isThreadStatic)
{
if (staticFields != null && (staticFields.Count > 0))
{
// Generate struct symbol for type describing individual fields of the statics region
ClassFieldsTypeDescriptor fieldsDescriptor = new ClassFieldsTypeDescriptor
{
Size = (ulong)0,
FieldsCount = staticFields.Count
};
ClassTypeDescriptor classTypeDescriptor = new ClassTypeDescriptor
{
IsStruct = !staticDataInObject ? 1 : 0,
Name = Utf8String.Concat("__type"u8, staticFieldForm.AsSpan(), _objectWriter.GetMangledName(defType).AsSpan()),
BaseClassId = 0
};
if (staticDataInObject)
{
classTypeDescriptor.BaseClassId = GetTypeIndex(defType.Context.GetWellKnownType(WellKnownType.Object), true);
}
uint staticFieldRegionTypeIndex = _objectWriter.GetCompleteClassTypeIndex(classTypeDescriptor, fieldsDescriptor, staticFields.ToArray(), null);
uint staticFieldRegionSymbolTypeIndex = staticFieldRegionTypeIndex;
if (isThreadStatic)
{
// Generate helper struct used by natvis to get to the actual thread static data
ClassFieldsTypeDescriptor helperFieldsDescriptor = new ClassFieldsTypeDescriptor
{
Size = (ulong)NodeFactory.Target.PointerSize * 2ul,
FieldsCount = 2
};
ClassTypeDescriptor helperClassTypeDescriptor = new ClassTypeDescriptor
{
IsStruct = 1,
Name = new Utf8String($"__ThreadStaticHelper<{classTypeDescriptor.Name}>"),
BaseClassId = 0
};
var pointerTypeDescriptor = new PointerTypeDescriptor
{
Is64Bit = Is64Bit ? 1 : 0,
IsConst = 0,
IsReference = 0,
ElementType = GetTypeIndex(defType.Context.SystemModule.GetType("Internal.Runtime.CompilerHelpers"u8, "TypeManagerSlot"u8), true)
};
var helperFields = new DataFieldDescriptor[] {
new DataFieldDescriptor
{
FieldTypeIndex = _objectWriter.GetPointerTypeIndex(pointerTypeDescriptor),
Offset = 0,
Name = new Utf8String("TypeManagerSlot"u8)
},
new DataFieldDescriptor
{
FieldTypeIndex = GetVariableTypeIndex(defType.Context.GetWellKnownType(Is64Bit? WellKnownType.Int64 : WellKnownType.Int32), true),
Offset = (ulong)NodeFactory.Target.PointerSize,
Name = new Utf8String("ClassIndex"u8)
}
};
staticFieldRegionTypeIndex = _objectWriter.GetCompleteClassTypeIndex(helperClassTypeDescriptor, helperFieldsDescriptor, helperFields, null);
staticFieldRegionSymbolTypeIndex = staticFieldRegionTypeIndex;
staticFieldForm = WindowsNodeMangler.ThreadStaticIndexName;
}
else if (staticDataInObject)// This means that access to this static region is done via indirection
{
PointerTypeDescriptor pointerTypeDescriptor = default(PointerTypeDescriptor);
pointerTypeDescriptor.Is64Bit = Is64Bit ? 1 : 0;
pointerTypeDescriptor.IsConst = 0;
pointerTypeDescriptor.IsReference = 0;
pointerTypeDescriptor.ElementType = staticFieldRegionTypeIndex;
staticFieldRegionSymbolTypeIndex = _objectWriter.GetPointerTypeIndex(pointerTypeDescriptor);
}
DataFieldDescriptor staticRegionField = new DataFieldDescriptor
{
FieldTypeIndex = staticFieldRegionSymbolTypeIndex,
Offset = 0xFFFFFFFF,
Name = staticFieldForm
};
fieldDescs.Add(staticRegionField);
}
}
private uint GetPrimitiveTypeIndex(TypeDesc type)
{
Debug.Assert(type.IsPrimitive, "it is not a primitive type");
uint typeIndex;
if (_primitiveTypes.TryGetValue(type, out typeIndex))
return typeIndex;
typeIndex = _objectWriter.GetPrimitiveTypeIndex(type);
_primitiveTypes[type] = typeIndex;
return typeIndex;
}
private ITypesDebugInfoWriter _objectWriter;
private Dictionary<TypeDesc, uint> _knownTypes = new Dictionary<TypeDesc, uint>();
private Dictionary<TypeDesc, uint> _completeKnownTypes = new Dictionary<TypeDesc, uint>();
private Dictionary<TypeDesc, uint> _knownReferenceWrappedTypes = new Dictionary<TypeDesc, uint>();
private Dictionary<TypeDesc, uint> _knownStateMachineThisTypes = new Dictionary<TypeDesc, uint>();
private Dictionary<TypeDesc, uint> _pointerTypes = new Dictionary<TypeDesc, uint>();
private Dictionary<TypeDesc, uint> _enumTypes = new Dictionary<TypeDesc, uint>();
private Dictionary<TypeDesc, uint> _byRefTypes = new Dictionary<TypeDesc, uint>();
private Dictionary<TypeDesc, uint> _thisTypes = new Dictionary<TypeDesc, uint>();
private Dictionary<TypeDesc, uint> _primitiveTypes = new Dictionary<TypeDesc, uint>();
private Dictionary<MethodDesc, uint> _methodIndices = new Dictionary<MethodDesc, uint>();
private Dictionary<MethodDesc, uint> _methodIdIndices = new Dictionary<MethodDesc, uint>();
public ICollection<KeyValuePair<TypeDesc, uint>> CompleteKnownTypes => _completeKnownTypes;
}
}
|