|
// 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.Immutable;
using System.Runtime.InteropServices;
using Microsoft.Diagnostics.DataContractReader.Contracts;
namespace Microsoft.Diagnostics.DataContractReader.Legacy;
// Port of native DacDbiInterfaceImpl::TypeDataWalk
//
// Walks the flattened DebuggerIPCE_TypeArgData[] tree that the right side built in
// CordbType::GatherTypeData and produces a TypeHandle for the loaded representation
// (exact, or canonical when generic code-sharing collapses reference type-args to
// System.__Canon and value type-args to their canonical form).
//
internal unsafe ref struct TypeDataWalk
{
private readonly Target _target;
private readonly IRuntimeTypeSystem _rts;
private readonly TypeHandle _canonTh;
private DebuggerIPCE_TypeArgData* _pCurrent;
private uint _remaining;
public TypeDataWalk(Target target, IRuntimeTypeSystem rts, TypeHandle canonTh, DebuggerIPCE_TypeArgData* pData, uint nData)
{
_target = target;
_rts = rts;
_canonTh = canonTh;
_pCurrent = pData;
_remaining = nData;
}
// Pop a single node from the head of the list, or null if exhausted.
private DebuggerIPCE_TypeArgData* ReadOne()
{
if (_remaining == 0)
return null;
_remaining--;
DebuggerIPCE_TypeArgData* p = _pCurrent;
_pCurrent++;
return p;
}
// Skip a single node and all of its descendants.
private void Skip()
{
DebuggerIPCE_TypeArgData* p = ReadOne();
if (p != null)
{
uint n = DacDbiImpl.ReadLittleEndian(p->numTypeArgs);
for (uint i = 0; i < n; i++)
Skip();
}
}
public TypeHandle ReadLoadedTypeHandle()
{
DebuggerIPCE_TypeArgData* p = ReadOne();
if (p == null)
return default;
CorElementType et = (CorElementType)DacDbiImpl.ReadLittleEndian(p->data.elementType);
switch (et)
{
case CorElementType.Array:
case CorElementType.SzArray:
return ArrayTypeArg(p);
case CorElementType.Ptr:
case CorElementType.Byref:
return PtrOrByRefTypeArg(p);
case CorElementType.Class:
case CorElementType.ValueType:
{
ulong vmAssembly = DacDbiImpl.ReadLittleEndian(p->data.ClassTypeData_vmAssembly);
uint metadataToken = DacDbiImpl.ReadLittleEndian(p->data.ClassTypeData_metadataToken);
return ReadLoadedInstantiation(vmAssembly, metadataToken, DacDbiImpl.ReadLittleEndian(p->numTypeArgs));
}
case CorElementType.FnPtr:
return FnPtrTypeArg(p);
default:
return _rts.GetPrimitiveType(et);
}
}
// Read a single type argument in canonicalization-aware fashion.
private TypeHandle ReadLoadedTypeArg()
{
DebuggerIPCE_TypeArgData* p = ReadOne();
if (p == null)
return default;
CorElementType et = (CorElementType)DacDbiImpl.ReadLittleEndian(p->data.elementType);
switch (et)
{
case CorElementType.Ptr:
return PtrOrByRefTypeArg(p);
case CorElementType.Class:
case CorElementType.ValueType:
return ClassTypeArg(p);
case CorElementType.FnPtr:
return FnPtrTypeArg(p);
default:
return ObjRefOrPrimitiveTypeArg(p, et);
}
}
// Read an instantiation and ask the runtime-type-system for the loaded handle.
private TypeHandle ReadLoadedInstantiation(ulong vmAssembly, uint metadataToken, uint nTypeArgs)
{
TypeHandle typeDef = TryLookupTypeDefOrRefInAssembly(vmAssembly, metadataToken);
if (typeDef.IsNull)
return default;
if (nTypeArgs == 0)
return typeDef;
ImmutableArray<TypeHandle>.Builder builder = ImmutableArray.CreateBuilder<TypeHandle>((int)nTypeArgs);
bool allOK = true;
for (uint i = 0; i < nTypeArgs; i++)
{
TypeHandle th = ReadLoadedTypeArg();
allOK &= !th.IsNull;
builder.Add(th);
}
if (!allOK)
return default;
return _rts.GetConstructedType(typeDef, CorElementType.GenericInst, 0, builder.MoveToImmutable());
}
private TypeHandle ArrayTypeArg(DebuggerIPCE_TypeArgData* pInfo)
{
TypeHandle elem = ReadLoadedTypeArg();
if (elem.IsNull)
return default;
CorElementType et = (CorElementType)DacDbiImpl.ReadLittleEndian(pInfo->data.elementType);
int rank = (int)DacDbiImpl.ReadLittleEndian(pInfo->data.ArrayTypeData_arrayRank);
return _rts.GetConstructedType(elem, et, rank, ImmutableArray<TypeHandle>.Empty);
}
private TypeHandle PtrOrByRefTypeArg(DebuggerIPCE_TypeArgData* pInfo)
{
TypeHandle referent = ReadLoadedTypeArg();
if (referent.IsNull)
return default;
CorElementType et = (CorElementType)DacDbiImpl.ReadLittleEndian(pInfo->data.elementType);
return _rts.GetConstructedType(referent, et, 0, ImmutableArray<TypeHandle>.Empty);
}
// A generic reference type collapses to System.__Canon
// (and its type arguments are skipped); a value-type instantiation is recursively
// resolved.
private TypeHandle ClassTypeArg(DebuggerIPCE_TypeArgData* pInfo)
{
ulong vmAssembly = DacDbiImpl.ReadLittleEndian(pInfo->data.ClassTypeData_vmAssembly);
uint metadataToken = DacDbiImpl.ReadLittleEndian(pInfo->data.ClassTypeData_metadataToken);
uint numTypeArgs = DacDbiImpl.ReadLittleEndian(pInfo->numTypeArgs);
CorElementType et = (CorElementType)DacDbiImpl.ReadLittleEndian(pInfo->data.elementType);
TypeHandle typeDef = TryLookupTypeDefOrRefInAssembly(vmAssembly, metadataToken);
if ((!typeDef.IsNull && _rts.IsValueType(typeDef)) || et == CorElementType.ValueType)
{
return ReadLoadedInstantiation(vmAssembly, metadataToken, numTypeArgs);
}
else
{
for (uint i = 0; i < numTypeArgs; i++)
Skip();
return _canonTh;
}
}
private TypeHandle FnPtrTypeArg(DebuggerIPCE_TypeArgData* pInfo)
{
uint numTypeArgs = DacDbiImpl.ReadLittleEndian(pInfo->numTypeArgs);
ImmutableArray<TypeHandle>.Builder builder = ImmutableArray.CreateBuilder<TypeHandle>((int)numTypeArgs);
bool allOK = true;
for (uint i = 0; i < numTypeArgs; i++)
{
TypeHandle th = ReadLoadedTypeArg();
allOK &= !th.IsNull;
builder.Add(th);
}
if (!allOK)
return default;
// Non-default calling conventions are not supported (matches the exact-handle path).
return _rts.GetConstructedType(default, CorElementType.FnPtr, 0, builder.MoveToImmutable());
}
private TypeHandle ObjRefOrPrimitiveTypeArg(DebuggerIPCE_TypeArgData* pInfo, CorElementType elementType)
{
// Skip any children: they are part of a reference-typed argument that canonicalizes to __Canon.
uint numTypeArgs = DacDbiImpl.ReadLittleEndian(pInfo->numTypeArgs);
for (uint i = 0; i < numTypeArgs; i++)
Skip();
if (_rts.IsCorElementTypeObjRef(elementType))
return _canonTh;
return _rts.GetPrimitiveType(elementType);
}
private TypeHandle TryLookupTypeDefOrRefInAssembly(ulong vmAssembly, uint metadataToken)
{
ILoader loader = _target.Contracts.Loader;
IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromAssemblyPtr(new TargetPointer(vmAssembly));
ModuleLookupTables lookupTables = loader.GetLookupTables(moduleHandle);
TargetPointer mt;
switch ((EcmaMetadataUtils.TokenType)(metadataToken & EcmaMetadataUtils.TokenTypeMask))
{
case EcmaMetadataUtils.TokenType.mdtTypeDef:
mt = loader.GetModuleLookupMapElement(lookupTables.TypeDefToMethodTable, metadataToken, out _);
break;
case EcmaMetadataUtils.TokenType.mdtTypeRef:
mt = loader.GetModuleLookupMapElement(lookupTables.TypeRefToMethodTable, metadataToken, out _);
break;
default:
return default;
}
if (mt == TargetPointer.Null)
return default;
return rts.GetTypeHandle(mt);
}
}
|