mirror of
https://github.com/VSadov/Satori.git
synced 2025-06-09 09:34:49 +09:00
cDac work necessary to get the MethodTableName (#104759)
Add new details to the RuntimeTypeSystem and Loader contracts as needed to load metadata and examine and identify all of the kinds of type that the CoreCLR type system can represent Add a type name generator based on a line by line copy from the CoreCLR codebase Add an Ecma335 metadata parser which is pure safe managed code, and is capable of loading metadata that is not structured as a single array. This implementation is heavily based on the dnmd implementation. It is intended that this implementation is short-lived, and really ought to be replaced by dnmd or System.Reflection.Metadata when those apis become suitable for use in this scenario. Provide implementations and documentation for all of the new contracts except for the RW metadata one. (Its rarely needed, so we can add it later) Enhance the target infrastructure to better handle various forms of arrays, and contracts which do math based on target pointer sizes. Contributes to https://github.com/dotnet/runtime/issues/99302
This commit is contained in:
parent
57f870f909
commit
22630598e4
44 changed files with 3977 additions and 178 deletions
66
docs/design/datacontracts/DacStreams.md
Normal file
66
docs/design/datacontracts/DacStreams.md
Normal file
|
@ -0,0 +1,66 @@
|
|||
# Contract DacStreams
|
||||
|
||||
This contract is for getting information from the streams embedded into a dump file as it crashes
|
||||
|
||||
## APIs of contract
|
||||
|
||||
``` csharp
|
||||
// Return string corresponding to type system data structure if it exists, or null otherwise
|
||||
string StringFromEEAddress(TargetPointer address);
|
||||
```
|
||||
|
||||
## Version 1
|
||||
|
||||
Global variables used
|
||||
| Global Name | Type | Purpose |
|
||||
| --- | --- | --- |
|
||||
| MiniMetaDataBuffAddress | TargetPointer | Identify where the mini metadata stream exists |
|
||||
| MiniMetaDataBuffMaxSize | uint | Identify where the size of the mini metadata stream |
|
||||
|
||||
Magic numbers
|
||||
| Name | Value |
|
||||
| --- | --- |
|
||||
| MiniMetadataSignature | 0x6d727473 |
|
||||
| EENameStreamSignature | 0x614e4545 |
|
||||
|
||||
The format of the MiniMetadataStream begins with a Streams header, which has 3 fields
|
||||
|
||||
| Field | Type | Offset | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| MiniMetadataSignature| uint | 0 | Magic value used to identify that there are streams |
|
||||
| TotalSize | uint | 4 | Total size of the entire set of MiniMetadata streams including this header |
|
||||
| Count of Streams | uint | 8 | Number of streams in the MiniMetadata |
|
||||
|
||||
The concept is that each stream simply follows the previous stream in the buffer.
|
||||
There is no padding, so the data is not expected to be aligned within the buffer.
|
||||
NOTE: At the moment there is only 1 supported stream type, so Count of Streams can only be 1.
|
||||
|
||||
The `EENameStream` is structured as a header, plus a series of null-terminated utf8 strings, and pointers.
|
||||
|
||||
The EENameStream header
|
||||
| Field | Type | Offset | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| EENameStreamSignature | uint | 0 | Magic value used to identify that the bytes immediately following are an `EENameStream` |
|
||||
| CountOfNames | uint | 4 | Number of names encoded |
|
||||
|
||||
EENameStream entry
|
||||
| Field | Type | Offset | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| Pointer | pointer | 0 | Pointer to type system structure |
|
||||
| String | null-terminated UTF-8 sting | 4 or 8 based on target pointer size | Pointer to type system structure |
|
||||
|
||||
Following the EENameStream header, there are CountOfNames entries. Each entry begins with a target pointer sized block which identifies a particular type system data structure, followed by a utf8 encoded null-terminated string.
|
||||
|
||||
``` csharp
|
||||
string StringFromEEAddress(TargetPointer address)
|
||||
{
|
||||
TargetPointer miniMetaDataBuffAddress = _target.Read<uint>(_target.ReadGlobalPointer(Constants.Globals.MiniMetaDataBuffAddress));
|
||||
uint miniMetaDataBuffMaxSize = _target.Read<uint>(_target.ReadGlobalPointer(Constants.Globals.MiniMetaDataBuffMaxSize));
|
||||
|
||||
// Parse MiniMetadataStream according the the format described above to produce a dictionary from pointer to string from the EENameStream.
|
||||
// Then lookup in the dictionary, to produce a result if it was present in the table.
|
||||
// In general, since this api is intended for fallback scenarios, implementations of this api should attempt
|
||||
// to return null instead of producing errors.
|
||||
// Since in normal execution of the runtime no stream is constructed, it is normal when examining full dumps and live process state without a stream encoded.
|
||||
}
|
||||
```
|
|
@ -26,6 +26,75 @@ record struct ModuleLookupTables(
|
|||
TargetPointer MethodDefToDesc,
|
||||
TargetPointer TypeDefToMethodTable,
|
||||
TargetPointer TypeRefToMethodTable);
|
||||
|
||||
internal struct EcmaMetadataSchema
|
||||
{
|
||||
public EcmaMetadataSchema(string metadataVersion, bool largeStringHeap, bool largeBlobHeap, bool largeGuidHeap, int[] rowCount, bool[] isSorted, bool variableSizedColumnsAre4BytesLong)
|
||||
{
|
||||
MetadataVersion = metadataVersion;
|
||||
LargeStringHeap = largeStringHeap;
|
||||
LargeBlobHeap = largeBlobHeap;
|
||||
LargeGuidHeap = largeGuidHeap;
|
||||
|
||||
_rowCount = rowCount;
|
||||
_isSorted = isSorted;
|
||||
|
||||
VariableSizedColumnsAreAll4BytesLong = variableSizedColumnsAre4BytesLong;
|
||||
}
|
||||
|
||||
public readonly string MetadataVersion;
|
||||
|
||||
public readonly bool LargeStringHeap;
|
||||
public readonly bool LargeBlobHeap;
|
||||
public readonly bool LargeGuidHeap;
|
||||
|
||||
// Table data, these structures hold MetadataTable.Count entries
|
||||
private readonly int[] _rowCount;
|
||||
public readonly ReadOnlySpan<int> RowCount => _rowCount;
|
||||
|
||||
private readonly bool[] _isSorted;
|
||||
public readonly ReadOnlySpan<bool> IsSorted => _isSorted;
|
||||
|
||||
// In certain scenarios the size of the tables is forced to be the maximum size
|
||||
// Otherwise the size of columns should be computed based on RowSize/the various heap flags
|
||||
public readonly bool VariableSizedColumnsAreAll4BytesLong;
|
||||
}
|
||||
|
||||
internal class TargetEcmaMetadata
|
||||
{
|
||||
public TargetEcmaMetadata(EcmaMetadataSchema schema,
|
||||
TargetSpan[] tables,
|
||||
TargetSpan stringHeap,
|
||||
TargetSpan userStringHeap,
|
||||
TargetSpan blobHeap,
|
||||
TargetSpan guidHeap)
|
||||
{
|
||||
Schema = schema;
|
||||
_tables = tables;
|
||||
StringHeap = stringHeap;
|
||||
UserStringHeap = userStringHeap;
|
||||
BlobHeap = blobHeap;
|
||||
GuidHeap = guidHeap;
|
||||
}
|
||||
|
||||
public EcmaMetadataSchema Schema { get; init; }
|
||||
|
||||
private TargetSpan[] _tables;
|
||||
public ReadOnlySpan<TargetSpan> Tables => _tables;
|
||||
public TargetSpan StringHeap { get; init; }
|
||||
public TargetSpan UserStringHeap { get; init; }
|
||||
public TargetSpan BlobHeap { get; init; }
|
||||
public TargetSpan GuidHeap { get; init; }
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum AvailableMetadataType
|
||||
{
|
||||
None = 0,
|
||||
ReadOnly = 1,
|
||||
ReadWriteSavedCopy = 2,
|
||||
ReadWrite = 4
|
||||
}
|
||||
```
|
||||
|
||||
``` csharp
|
||||
|
@ -36,13 +105,31 @@ TargetPointer GetLoaderAllocator(ModuleHandle handle);
|
|||
TargetPointer GetThunkHeap(ModuleHandle handle);
|
||||
TargetPointer GetILBase(ModuleHandle handle);
|
||||
TargetPointer GetMetadataAddress(ModuleHandle handle, out ulong size);
|
||||
AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle);
|
||||
TargetPointer GetReadWriteSavedMetadataAddress(ModuleHandle handle, out ulong size);
|
||||
TargetEcmaMetadata GetReadWriteMetadata(ModuleHandle handle);
|
||||
ModuleLookupTables GetLookupTables(ModuleHandle handle);
|
||||
```
|
||||
|
||||
## Version 1
|
||||
|
||||
Data descriptors used:
|
||||
- `Module`
|
||||
| Data Descriptor Name | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `Module` | `Assembly` | Assembly of the Module |
|
||||
| `Module` | `Base` | Pointer to start of PE file in memory |
|
||||
| `Module` | `Flags` | Assembly of the Module |
|
||||
| `Module` | `LoaderAllocator` | LoaderAllocator of the Module |
|
||||
| `Module` | `ThunkHeap` | Pointer to the thunk heap |
|
||||
| `Module` | `DynamicMetadata` | Pointer to saved metadata for reflection emit modules |
|
||||
| `Module` | `FieldDefToDescMap` | Mapping table |
|
||||
| `Module` | `ManifestModuleReferencesMap` | Mapping table |
|
||||
| `Module` | `MemberRefToDescMap` | Mapping table |
|
||||
| `Module` | `MethodDefToDescMap` | Mapping table |
|
||||
| `Module` | `TypeDefToMethodTableMap` | Mapping table |
|
||||
| `Module` | `TypeRefToMethodTableMap` | Mapping table |
|
||||
| `DynamicMetadata` | `Size` | Size of the dynamic metadata blob (as a 32bit uint) |
|
||||
| `DynamicMetadata` | `Data` | Start of dynamic metadata data array |
|
||||
|
||||
``` csharp
|
||||
ModuleHandle GetModuleHandle(TargetPointer modulePointer)
|
||||
|
@ -94,6 +181,32 @@ TargetPointer GetMetadataAddress(ModuleHandle handle, out ulong size)
|
|||
return baseAddress + rva;
|
||||
}
|
||||
|
||||
AvailableMetadataType ILoader.GetAvailableMetadataType(ModuleHandle handle)
|
||||
{
|
||||
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
|
||||
|
||||
AvailableMetadataType flags = AvailableMetadataType.None;
|
||||
|
||||
TargetPointer dynamicMetadata = target.ReadPointer(handle.Address + /* Module::DynamicMetadata offset */);
|
||||
|
||||
if (dynamicMetadata != TargetPointer.Null)
|
||||
flags |= AvailableMetadataType.ReadWriteSavedCopy;
|
||||
else
|
||||
flags |= AvailableMetadataType.ReadOnly;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
TargetPointer ILoader.GetReadWriteSavedMetadataAddress(ModuleHandle handle, out ulong size)
|
||||
{
|
||||
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
|
||||
TargetPointer dynamicMetadata = target.ReadPointer(handle.Address + /* Module::DynamicMetadata offset */);
|
||||
|
||||
size = target.Read<uint>(handle.Address + /* DynamicMetadata::Size offset */);
|
||||
TargetPointer result = handle.Address + /* DynamicMetadata::Data offset */;
|
||||
return result;
|
||||
}
|
||||
|
||||
ModuleLookupTables GetLookupTables(ModuleHandle handle)
|
||||
{
|
||||
return new ModuleLookupTables(
|
||||
|
|
|
@ -4,46 +4,75 @@ This contract is for exploring the properties of the runtime types of values on
|
|||
|
||||
## APIs of contract
|
||||
|
||||
A `MethodTable` is the runtime representation of the type information about a value. Given a `TargetPointer` address, the `RuntimeTypeSystem` contract provides a `MethodTableHandle` for querying the `MethodTable`.
|
||||
A `TypeHandle` is the runtime representation of the type information about a value which is represented as a TypeHandle.
|
||||
Given a `TargetPointer` address, the `RuntimeTypeSystem` contract provides a `TypeHandle` for querying the details of the `TypeHandle`.
|
||||
|
||||
|
||||
``` csharp
|
||||
struct MethodTableHandle
|
||||
struct TypeHandle
|
||||
{
|
||||
// no public properties or constructors
|
||||
// no public constructors
|
||||
|
||||
internal TargetPointer Address { get; }
|
||||
public TargetPointer Address { get; }
|
||||
public bool IsNull => Address != 0;
|
||||
}
|
||||
|
||||
internal enum CorElementType
|
||||
{
|
||||
// Values defined in ECMA-335 - II.23.1.16 Element types used in signatures
|
||||
// +
|
||||
Internal = 0x21, // Indicates that the next pointer sized number of bytes is the address of a TypeHandle. Signatures that contain the Internal CorElementType cannot exist in metadata that is saved into a serialized format.
|
||||
}
|
||||
```
|
||||
|
||||
``` csharp
|
||||
#region MethodTable inspection APIs
|
||||
public virtual MethodTableHandle GetMethodTableHandle(TargetPointer targetPointer);
|
||||
A `TypeHandle` is the runtime representation of the type information about a value. This can be constructed from the address of a `TypeHandle` or a `MethodTable`.
|
||||
|
||||
public virtual TargetPointer GetModule(MethodTableHandle methodTable);
|
||||
``` csharp
|
||||
#region TypeHandle inspection APIs
|
||||
public virtual TypeHandle GetTypeHandle(TargetPointer targetPointer);
|
||||
|
||||
public virtual TargetPointer GetModule(TypeHandle typeHandle);
|
||||
// A canonical method table is either the MethodTable itself, or in the case of a generic instantiation, it is the
|
||||
// MethodTable of the prototypical instance.
|
||||
public virtual TargetPointer GetCanonicalMethodTable(MethodTableHandle methodTable);
|
||||
public virtual TargetPointer GetParentMethodTable(MethodTableHandle methodTable);
|
||||
public virtual TargetPointer GetCanonicalMethodTable(TypeHandle typeHandle);
|
||||
public virtual TargetPointer GetParentMethodTable(TypeHandle typeHandle);
|
||||
|
||||
public virtual uint GetBaseSize(MethodTableHandle methodTable);
|
||||
public virtual uint GetBaseSize(TypeHandle typeHandle);
|
||||
// The component size is only available for strings and arrays. It is the size of the element type of the array, or the size of an ECMA 335 character (2 bytes)
|
||||
public virtual uint GetComponentSize(MethodTableHandle methodTable);
|
||||
public virtual uint GetComponentSize(TypeHandle typeHandle);
|
||||
|
||||
// True if the MethodTable is the sentinel value associated with unallocated space in the managed heap
|
||||
public virtual bool IsFreeObjectMethodTable(MethodTableHandle methodTable);
|
||||
public virtual bool IsString(MethodTableHandle methodTable);
|
||||
public virtual bool IsFreeObjectMethodTable(TypeHandle typeHandle);
|
||||
public virtual bool IsString(TypeHandle typeHandle);
|
||||
// True if the MethodTable represents a type that contains managed references
|
||||
public virtual bool ContainsGCPointers(MethodTableHandle methodTable);
|
||||
public virtual bool IsDynamicStatics(MethodTableHandle methodTable);
|
||||
public virtual ushort GetNumMethods(MethodTableHandle methodTable);
|
||||
public virtual ushort GetNumInterfaces(MethodTableHandle methodTable);
|
||||
public virtual bool ContainsGCPointers(TypeHandle typeHandle);
|
||||
public virtual bool IsDynamicStatics(TypeHandle typeHandle);
|
||||
public virtual ushort GetNumMethods(TypeHandle typeHandle);
|
||||
public virtual ushort GetNumInterfaces(TypeHandle typeHandle);
|
||||
|
||||
// Returns an ECMA-335 TypeDef table token for this type, or for its generic type definition if it is a generic instantiation
|
||||
public virtual uint GetTypeDefToken(MethodTableHandle methodTable);
|
||||
public virtual uint GetTypeDefToken(TypeHandle typeHandle);
|
||||
// Returns the ECMA 335 TypeDef table Flags value (a bitmask of TypeAttributes) for this type,
|
||||
// or for its generic type definition if it is a generic instantiation
|
||||
public virtual uint GetTypeDefTypeAttributes(MethodTableHandle methodTable);
|
||||
#endregion MethodTable inspection APIs
|
||||
public virtual uint GetTypeDefTypeAttributes(TypeHandle typeHandle);
|
||||
public virtual ReadOnlySpan<TypeHandle> GetInstantiation(TypeHandle typeHandle);
|
||||
public virtual bool IsGenericTypeDefinition(TypeHandle typeHandle);
|
||||
|
||||
public virtual TypeHandle TypeHandleFromAddress(TargetPointer address);
|
||||
public virtual bool HasTypeParam(TypeHandle typeHandle);
|
||||
|
||||
// Element type of the type. NOTE: this drops the CorElementType.GenericInst, and CorElementType.String is returned as CorElementType.Class.
|
||||
// NOTE: If this returns CorElementType.ValueType it may be a normal valuetype or a "NATIVE" valuetype used to represent an interop view of a structure
|
||||
// HasTypeParam will return true for cases where this is the interop view, and false for normal valuetypes.
|
||||
public virtual CorElementType GetSignatureCorElementType(TypeHandle typeHandle);
|
||||
|
||||
// return true if the TypeHandle represents an array, and set the rank to either 0 (if the type is not an array), or the rank number if it is.
|
||||
public virtual bool IsArray(TypeHandle typeHandle, out uint rank);
|
||||
public virtual TypeHandle GetTypeParam(TypeHandle typeHandle);
|
||||
public virtual bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token);
|
||||
public virtual bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv);
|
||||
|
||||
#endregion TypeHandle inspection APIs
|
||||
```
|
||||
|
||||
## Version 1
|
||||
|
@ -60,6 +89,7 @@ internal partial struct RuntimeTypeSystem_1
|
|||
{
|
||||
GenericsMask = 0x00000030,
|
||||
GenericsMask_NonGeneric = 0x00000000, // no instantiation
|
||||
GenericsMask_TypicalInstantiation = 0x00000030, // the type instantiated at its formal parameters, e.g. List<T>
|
||||
|
||||
StringArrayValues = GenericsMask_NonGeneric,
|
||||
}
|
||||
|
@ -69,9 +99,17 @@ internal partial struct RuntimeTypeSystem_1
|
|||
internal enum WFLAGS_HIGH : uint
|
||||
{
|
||||
Category_Mask = 0x000F0000,
|
||||
Category_Array = 0x00080000,
|
||||
Category_ElementType_Mask = 0x000E0000,
|
||||
Category_Array_Mask = 0x000C0000,
|
||||
|
||||
Category_IfArrayThenSzArray = 0x00020000,
|
||||
Category_Array = 0x00080000,
|
||||
Category_ValueType = 0x00040000,
|
||||
Category_Nullable = 0x00050000,
|
||||
Category_PrimitiveValueType = 0x00060000,
|
||||
Category_TruePrimitive = 0x00070000,
|
||||
Category_Interface = 0x000C0000,
|
||||
|
||||
ContainsGCPointers = 0x01000000,
|
||||
HasComponentSize = 0x80000000, // This is set if lower 16 bits is used for the component size,
|
||||
// otherwise the lower bits are used for WFLAGS_LOW
|
||||
|
@ -118,6 +156,7 @@ internal partial struct RuntimeTypeSystem_1
|
|||
public bool HasInstantiation => !TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_NonGeneric);
|
||||
public bool ContainsGCPointers => GetFlag(WFLAGS_HIGH.ContainsGCPointers) != 0;
|
||||
public bool IsDynamicStatics => GetFlag(WFLAGS2_ENUM.DynamicStatics) != 0;
|
||||
public bool IsGenericTypeDefinition => TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_TypicalInstantiation);
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
@ -127,6 +166,16 @@ internal partial struct RuntimeTypeSystem_1
|
|||
CanonMT = 1,
|
||||
Mask = 1,
|
||||
}
|
||||
|
||||
// Low order bits of TypeHandle address.
|
||||
// If the low bits contain a 2, then it is a TypeDesc
|
||||
[Flags]
|
||||
internal enum TypeHandleBits
|
||||
{
|
||||
MethodTable = 0,
|
||||
TypeDesc = 2,
|
||||
ValidMask = 2,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -141,6 +190,7 @@ internal struct MethodTable_1
|
|||
internal TargetPointer ParentMethodTable { get; }
|
||||
internal TargetPointer Module { get; }
|
||||
internal TargetPointer EEClassOrCanonMT { get; }
|
||||
internal TargetPointer PerInstInfo { get; }
|
||||
internal MethodTable_1(Data.MethodTable data)
|
||||
{
|
||||
Flags = new RuntimeTypeSystem_1.MethodTableFlags
|
||||
|
@ -154,24 +204,77 @@ internal struct MethodTable_1
|
|||
EEClassOrCanonMT = data.EEClassOrCanonMT;
|
||||
Module = data.Module;
|
||||
ParentMethodTable = data.ParentMethodTable;
|
||||
PerInstInfo = data.PerInstInfo;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Internally the contract uses extension methods on the `TypeHandle` api so that it can distinguish between `MethodTable` and `TypeDesc`
|
||||
```csharp
|
||||
static class RuntimeTypeSystem_1_Helpers
|
||||
{
|
||||
public static bool IsTypeDesc(this TypeHandle type)
|
||||
{
|
||||
return type.Address != 0 && (type.Address & TypeHandleBits.ValidMask) == TypeHandleBits.TypeDesc;
|
||||
}
|
||||
|
||||
public static bool IsMethodTable(this TypeHandle type)
|
||||
{
|
||||
return type.Address != 0 && (type.Address & TypeHandleBits.ValidMask) == TypeHandleBits.MethodTable;
|
||||
}
|
||||
|
||||
public static TargetPointer TypeDescAddress(this TypeHandle type)
|
||||
{
|
||||
if (!type.IsTypeDesc())
|
||||
return 0;
|
||||
|
||||
return (ulong)type.Address & ~(ulong)TypeHandleBits.ValidMask;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The contract depends on the global pointer value `FreeObjectMethodTablePointer`.
|
||||
The contract additionally depends on the `EEClass` data descriptor.
|
||||
|
||||
The contract additionally depends on these data descriptors
|
||||
|
||||
| Data Descriptor Name | Field | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `MethodTable` | `MTFlags` | One of the flags fields on `MethodTable` |
|
||||
| `MethodTable` | `MTFlags2` | One of the flags fields on `MethodTable` |
|
||||
| `MethodTable` | `BaseSize` | BaseSize of a `MethodTable` |
|
||||
| `MethodTable` | `EEClassOrCanonMT` | Path to both EEClass and canonical MethodTable of a MethodTable |
|
||||
| `MethodTable` | `Module` | Module for `MethodTable` |
|
||||
| `MethodTable` | `ParentMethodTable` | Parent type pointer of `MethodTable` |
|
||||
| `MethodTable` | `NumInterfaces` | Number of interfaces of `MethodTable` |
|
||||
| `MethodTable` | `NumVirtuals` | Number of virtual methods in `MethodTable` |
|
||||
| `MethodTable` | `PerInstInfo` | Either the array element type, or pointer to generic information for `MethodTable` |
|
||||
| `EEClass` | `InternalCorElementType` | An InternalCorElementType uses the enum values of a CorElementType to indicate some of the information about the type of the type which uses the EEClass In particular, all reference types are CorElementType.Class, Enums are the element type of their underlying type and ValueTypes which can exactly be represented as an element type are represented as such, all other values types are represented as CorElementType.ValueType. |
|
||||
| `EEClass` | `MethodTable` | Pointer to the canonical MethodTable of this type |
|
||||
| `EEClass` | `NumMethods` | Count of methods attached to the EEClass |
|
||||
| `EEClass` | `CorTypeAttr` | Various flags |
|
||||
| `ArrayClass` | `Rank` | Rank of the associated array MethodTable |
|
||||
| `TypeDesc` | `TypeAndFlags` | The lower 8 bits are the CorElementType of the `TypeDesc`, the upper 24 bits are reserved for flags |
|
||||
| `ParamTypeDesc` | `TypeArg` | Associated type argument |
|
||||
| `TypeVarTypeDesc` | `Module` | Pointer to module which defines the type variable |
|
||||
| `TypeVarTypeDesc` | `Token` | Token of the type variable |
|
||||
| `FnPtrTypeDesc` | `NumArgs` | Number of arguments to the function described by the `TypeDesc` |
|
||||
| `FnPtrTypeDesc` | `CallConv` | Lower 8 bits is the calling convention bit as extracted by the signature that defines this `TypeDesc` |
|
||||
| `FnPtrTypeDesc` | `RetAndArgTypes` | Pointer to an array of TypeHandle addresses. This length of this is 1 more than `NumArgs` |
|
||||
| `GenericsDictInfo` | `NumTypeArgs` | Number of type arguments in the type or method instantiation described by this `GenericsDictInfo` |
|
||||
|
||||
|
||||
```csharp
|
||||
private readonly Dictionary<TargetPointer, MethodTable_1> _methodTables;
|
||||
|
||||
internal TargetPointer FreeObjectMethodTablePointer {get; }
|
||||
|
||||
public MethodTableHandle GetMethodTableHandle(TargetPointer methodTablePointer)
|
||||
public TypeHandle GetTypeHandle(TargetPointer typeHandlePointer)
|
||||
{
|
||||
... // validate that methodTablePointer points to something that looks like a MethodTable.
|
||||
... // read Data.MethodTable from methodTablePointer.
|
||||
... // create a MethodTable_1 and add it to _methodTables.
|
||||
return MethodTableHandle { Address = methodTablePointer }
|
||||
... // validate that typeHandlePointer points to something that looks like a MethodTable or a TypeDesc.
|
||||
... // If this is a MethodTable
|
||||
... // read Data.MethodTable from typeHandlePointer.
|
||||
... // create a MethodTable_1 and add it to _methodTables.
|
||||
return TypeHandle { Address = typeHandlePointer }
|
||||
}
|
||||
|
||||
internal static EEClassOrCanonMTBits GetEEClassOrCanonMTBits(TargetPointer eeClassOrCanonMTPtr)
|
||||
|
@ -179,11 +282,11 @@ The contract additionally depends on the `EEClass` data descriptor.
|
|||
return (EEClassOrCanonMTBits)(eeClassOrCanonMTPtr & (ulong)EEClassOrCanonMTBits.Mask);
|
||||
}
|
||||
|
||||
public uint GetBaseSize(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.BaseSize;
|
||||
public uint GetBaseSize(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? (uint)0 : _methodTables[TypeHandle.Address].Flags.BaseSize;
|
||||
|
||||
public uint GetComponentSize(MethodTableHandle methodTableHandle) => GetComponentSize(_methodTables[methodTableHandle.Address]);
|
||||
public uint GetComponentSize(TypeHandle TypeHandle) =>!typeHandle.IsMethodTable() ? (uint)0 : GetComponentSize(_methodTables[TypeHandle.Address]);
|
||||
|
||||
private TargetPointer GetClassPointer(MethodTableHandle methodTableHandle)
|
||||
private TargetPointer GetClassPointer(TypeHandle TypeHandle)
|
||||
{
|
||||
... // if the MethodTable stores a pointer to the EEClass, return it
|
||||
// otherwise the MethodTable stores a pointer to the canonical MethodTable
|
||||
|
@ -191,34 +294,232 @@ The contract additionally depends on the `EEClass` data descriptor.
|
|||
// Canonical MethodTables always store an EEClass pointer.
|
||||
}
|
||||
|
||||
private Data.EEClass GetClassData(MethodTableHandle methodTableHandle)
|
||||
private Data.EEClass GetClassData(TypeHandle TypeHandle)
|
||||
{
|
||||
TargetPointer eeClassPtr = GetClassPointer(methodTableHandle);
|
||||
TargetPointer eeClassPtr = GetClassPointer(TypeHandle);
|
||||
... // read Data.EEClass data from eeClassPtr
|
||||
}
|
||||
|
||||
|
||||
public TargetPointer GetCanonicalMethodTable(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).MethodTable;
|
||||
public TargetPointer GetCanonicalMethodTable(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : GetClassData(TypeHandle).MethodTable;
|
||||
|
||||
public TargetPointer GetModule(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Module;
|
||||
public TargetPointer GetParentMethodTable(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].ParentMethodTable;
|
||||
|
||||
public bool IsFreeObjectMethodTable(MethodTableHandle methodTableHandle) => FreeObjectMethodTablePointer == methodTableHandle.Address;
|
||||
|
||||
public bool IsString(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.IsString;
|
||||
public bool ContainsGCPointers(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.ContainsGCPointers;
|
||||
|
||||
public uint GetTypeDefToken(MethodTableHandle methodTableHandle)
|
||||
public TargetPointer GetModule(TypeHandle TypeHandle)
|
||||
{
|
||||
MethodTable_1 methodTable = _methodTables[methodTableHandle.Address];
|
||||
return (uint)(methodTable.Flags.GetTypeDefRid() | ((int)TableIndex.TypeDef << 24));
|
||||
if (typeHandle.IsMethodTable())
|
||||
{
|
||||
return _methodTables[TypeHandle.Address].Module;
|
||||
}
|
||||
else if (typeHandle.IsTypeDesc())
|
||||
{
|
||||
if (HasTypeParam(typeHandle))
|
||||
{
|
||||
return GetModule(GetTypeParam(typeHandle));
|
||||
}
|
||||
else if (IsGenericVariable(typeHandle, out TargetPointer genericParamModule, out _))
|
||||
{
|
||||
return genericParamModule;
|
||||
}
|
||||
}
|
||||
return TargetPointer.Null;
|
||||
}
|
||||
|
||||
public ushort GetNumMethods(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).NumMethods;
|
||||
public TargetPointer GetParentMethodTable(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : _methodTables[TypeHandle.Address].ParentMethodTable;
|
||||
|
||||
public ushort GetNumInterfaces(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].NumInterfaces;
|
||||
public bool IsFreeObjectMethodTable(TypeHandle TypeHandle) => FreeObjectMethodTablePointer == TypeHandle.Address;
|
||||
|
||||
public uint GetTypeDefTypeAttributes(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).CorTypeAttr;
|
||||
public bool IsString(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[TypeHandle.Address].Flags.IsString;
|
||||
public bool ContainsGCPointers(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[TypeHandle.Address].Flags.ContainsGCPointers;
|
||||
|
||||
public bool IsDynamicStatics(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.IsDynamicStatics;
|
||||
public uint GetTypeDefToken(TypeHandle TypeHandle)
|
||||
{
|
||||
if (!typeHandle.IsMethodTable())
|
||||
return 0;
|
||||
|
||||
MethodTable_1 typeHandle = _methodTables[TypeHandle.Address];
|
||||
return (uint)(typeHandle.Flags.GetTypeDefRid() | ((int)TableIndex.TypeDef << 24));
|
||||
}
|
||||
|
||||
public ushort GetNumMethods(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? 0 : GetClassData(TypeHandle).NumMethods;
|
||||
|
||||
public ushort GetNumInterfaces(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? 0 : _methodTables[TypeHandle.Address].NumInterfaces;
|
||||
|
||||
public uint GetTypeDefTypeAttributes(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? 0 : GetClassData(TypeHandle).CorTypeAttr;
|
||||
|
||||
public bool IsDynamicStatics(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[TypeHandle.Address].Flags.IsDynamicStatics;
|
||||
|
||||
public ReadOnlySpan<TypeHandle> GetInstantiation(TypeHandle TypeHandle)
|
||||
{
|
||||
if (!typeHandle.IsMethodTable())
|
||||
return default;
|
||||
|
||||
MethodTable_1 typeHandle = _methodTables[TypeHandle.Address];
|
||||
if (!typeHandle.Flags.HasInstantiation)
|
||||
return default;
|
||||
|
||||
TargetPointer perInstInfo = typeHandle.PerInstInfo;
|
||||
TargetPointer genericsDictInfo = perInstInfo - (ulong)_target.PointerSize;
|
||||
TargetPointer dictionaryPointer = _target.ReadPointer(perInstInfo);
|
||||
|
||||
int NumTypeArgs = // Read NumTypeArgs from genericsDictInfo using GenericsDictInfo contract
|
||||
TypeHandle[] instantiation = new TypeHandle[NumTypeArgs];
|
||||
for (int i = 0; i < NumTypeArgs; i++)
|
||||
instantiation[i] = GetTypeHandle(_target.ReadPointer(dictionaryPointer + _target.PointerSize * i));
|
||||
|
||||
return instantiation;
|
||||
}
|
||||
|
||||
public bool IsDynamicStatics(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[TypeHandle.Address].Flags.IsDynamicStatics;
|
||||
|
||||
public bool HasTypeParam(TypeHandle typeHandle)
|
||||
{
|
||||
if (typeHandle.IsMethodTable())
|
||||
{
|
||||
MethodTable typeHandle = _methodTables[typeHandle.Address];
|
||||
return typeHandle.Flags.IsArray;
|
||||
}
|
||||
else if (typeHandle.IsTypeDesc())
|
||||
{
|
||||
int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
|
||||
CorElementType elemType = (CorElementType)(TypeAndFlags & 0xFF);
|
||||
switch (elemType)
|
||||
{
|
||||
case CorElementType.ValueType:
|
||||
case CorElementType.Byref:
|
||||
case CorElementType.Ptr:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public CorElementType GetSignatureCorElementType(TypeHandle typeHandle)
|
||||
{
|
||||
if (typeHandle.IsMethodTable())
|
||||
{
|
||||
MethodTable typeHandle = _methodTables[typeHandle.Address];
|
||||
|
||||
switch (typeHandle.Flags.GetFlag(WFLAGS_HIGH.Category_Mask))
|
||||
{
|
||||
case WFLAGS_HIGH.Category_Array:
|
||||
return CorElementType.Array;
|
||||
case WFLAGS_HIGH.Category_Array | WFLAGS_HIGH.Category_IfArrayThenSzArray:
|
||||
return CorElementType.SzArray;
|
||||
case WFLAGS_HIGH.Category_ValueType:
|
||||
case WFLAGS_HIGH.Category_Nullable:
|
||||
case WFLAGS_HIGH.Category_PrimitiveValueType:
|
||||
return CorElementType.ValueType;
|
||||
case WFLAGS_HIGH.Category_TruePrimitive:
|
||||
return (CorElementType)GetClassData(typeHandle).InternalCorElementType;
|
||||
default:
|
||||
return CorElementType.Class;
|
||||
}
|
||||
}
|
||||
else if (typeHandle.IsTypeDesc())
|
||||
{
|
||||
int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
|
||||
return (CorElementType)(TypeAndFlags & 0xFF);
|
||||
}
|
||||
return default(CorElementType);
|
||||
}
|
||||
|
||||
// return true if the TypeHandle represents an array, and set the rank to either 0 (if the type is not an array), or the rank number if it is.
|
||||
public bool IsArray(TypeHandle typeHandle, out uint rank)
|
||||
{
|
||||
if (typeHandle.IsMethodTable())
|
||||
{
|
||||
MethodTable typeHandle = _methodTables[typeHandle.Address];
|
||||
|
||||
switch (typeHandle.Flags.GetFlag(WFLAGS_HIGH.Category_Mask))
|
||||
{
|
||||
case WFLAGS_HIGH.Category_Array:
|
||||
TargetPointer clsPtr = GetClassPointer(typeHandle);
|
||||
rank = // Read Rank field from ArrayClass contract using address clsPtr
|
||||
return true;
|
||||
|
||||
case WFLAGS_HIGH.Category_Array | WFLAGS_HIGH.Category_IfArrayThenSzArray:
|
||||
rank = 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
rank = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public TypeHandle GetTypeParam(TypeHandle typeHandle)
|
||||
{
|
||||
if (typeHandle.IsMethodTable())
|
||||
{
|
||||
MethodTable typeHandle = _methodTables[typeHandle.Address];
|
||||
|
||||
// Validate that this is an array
|
||||
if (!typeHandle.Flags.IsArray)
|
||||
throw new ArgumentException(nameof(typeHandle));
|
||||
|
||||
return TypeHandleFromAddress(typeHandle.PerInstInfo);
|
||||
}
|
||||
else if (typeHandle.IsTypeDesc())
|
||||
{
|
||||
int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
|
||||
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
|
||||
|
||||
switch (elemType)
|
||||
{
|
||||
case CorElementType.ValueType:
|
||||
case CorElementType.Byref:
|
||||
case CorElementType.Ptr:
|
||||
TargetPointer typeArgPointer = // Read TypeArg field from ParamTypeDesc contract using address typeHandle.TypeDescAddress()
|
||||
return TypeHandleFromAddress(typeArgPointer);
|
||||
}
|
||||
}
|
||||
throw new ArgumentException(nameof(typeHandle));
|
||||
}
|
||||
|
||||
public bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token)
|
||||
{
|
||||
module = TargetPointer.Null;
|
||||
token = 0;
|
||||
|
||||
if (!typeHandle.IsTypeDesc())
|
||||
return false;
|
||||
|
||||
int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
|
||||
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
|
||||
|
||||
switch (elemType)
|
||||
{
|
||||
case CorElementType.MVar:
|
||||
case CorElementType.Var:
|
||||
module = // Read Module field from TypeVarTypeDesc contract using address typeHandle.TypeDescAddress()
|
||||
token = // Read Module field from TypeVarTypeDesc contract using address typeHandle.TypeDescAddress()
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv)
|
||||
{
|
||||
retAndArgTypes = default;
|
||||
callConv = default;
|
||||
|
||||
if (!typeHandle.IsTypeDesc())
|
||||
return false;
|
||||
|
||||
int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
|
||||
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
|
||||
|
||||
if (elemType != CorElementType.FnPtr)
|
||||
return false;
|
||||
|
||||
int NumArgs = // Read NumArgs field from FnPtrTypeDesc contract using address typeHandle.TypeDescAddress()
|
||||
TargetPointer RetAndArgTypes = // Read NumArgs field from FnPtrTypeDesc contract using address typeHandle.TypeDescAddress()
|
||||
|
||||
TypeHandle[] retAndArgTypesArray = new TypeHandle[NumTypeArgs + 1];
|
||||
for (int i = 0; i <= NumTypeArgs; i++)
|
||||
retAndArgTypesArray[i] = GetTypeHandle(_target.ReadPointer(RetAndArgTypes + _target.PointerSize * i));
|
||||
|
||||
retAndArgTypes = retAndArgTypesArray;
|
||||
callConv = (byte) // Read CallConv field from FnPtrTypeDesc contract using address typeHandle.TypeDescAddress(), and then ignore all but the low 8 bits.
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
|
|
@ -6718,11 +6718,11 @@ ClrDataAccess::GetMDImport(const PEAssembly* pPEAssembly, const ReflectionModule
|
|||
else if (reflectionModule != NULL)
|
||||
{
|
||||
// Get the metadata
|
||||
PTR_SBuffer metadataBuffer = reflectionModule->GetDynamicMetadataBuffer();
|
||||
TADDR metadataBuffer = reflectionModule->GetDynamicMetadataBuffer();
|
||||
if (metadataBuffer != PTR_NULL)
|
||||
{
|
||||
mdBaseTarget = dac_cast<PTR_CVOID>((metadataBuffer->DacGetRawBuffer()).StartAddress());
|
||||
mdSize = metadataBuffer->GetSize();
|
||||
mdBaseTarget = dac_cast<PTR_CVOID>(metadataBuffer + offsetof(DynamicMetadata, Data));
|
||||
mdSize = dac_cast<DPTR(DynamicMetadata)>(metadataBuffer)->Size;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -4285,7 +4285,10 @@ void DacDbiInterfaceImpl::GetMetadata(VMPTR_Module vmModule, TargetBuffer * pTar
|
|||
{
|
||||
// Here is the fetch.
|
||||
ReflectionModule * pReflectionModule = pModule->GetReflectionModule();
|
||||
InitTargetBufferFromTargetSBuffer(pReflectionModule->GetDynamicMetadataBuffer(), pTargetBuffer);
|
||||
|
||||
TADDR metadataBuffer = pReflectionModule->GetDynamicMetadataBuffer();
|
||||
CORDB_ADDRESS addr = PTR_TO_CORDB_ADDRESS(metadataBuffer + offsetof(DynamicMetadata, Data));
|
||||
pTargetBuffer->Init(addr, dac_cast<DPTR(DynamicMetadata)>(metadataBuffer)->Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1239,6 +1239,7 @@ public:
|
|||
HRESULT GetNestedExceptionDataImpl(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS *exceptionObject, CLRDATA_ADDRESS *nextNestedException);
|
||||
HRESULT GetMethodTableDataImpl(CLRDATA_ADDRESS mt, struct DacpMethodTableData *data);
|
||||
HRESULT GetMethodTableForEEClassImpl (CLRDATA_ADDRESS eeClassReallyMT, CLRDATA_ADDRESS *value);
|
||||
HRESULT GetMethodTableNameImpl(CLRDATA_ADDRESS mt, unsigned int count, _Inout_updates_z_(count) WCHAR *mtName, unsigned int *pNeeded);
|
||||
HRESULT GetObjectExceptionDataImpl(CLRDATA_ADDRESS objAddr, struct DacpExceptionObjectData *data);
|
||||
|
||||
BOOL IsExceptionFromManagedCode(EXCEPTION_RECORD * pExceptionRecord);
|
||||
|
|
|
@ -2015,7 +2015,47 @@ ClrDataAccess::GetMethodTableName(CLRDATA_ADDRESS mt, unsigned int count, _Inout
|
|||
return E_INVALIDARG;
|
||||
|
||||
SOSDacEnter();
|
||||
if (m_cdacSos != NULL)
|
||||
{
|
||||
// Try the cDAC first - it will return E_NOTIMPL if it doesn't support this method yet. Fall back to the DAC.
|
||||
hr = m_cdacSos->GetMethodTableName(mt, count, mtName, pNeeded);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
hr = GetMethodTableNameImpl(mt, count, mtName, pNeeded);
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
else
|
||||
{
|
||||
// Assert that the data is the same as what we get from the DAC.
|
||||
NewArrayHolder<WCHAR> pwszNameLocal(new WCHAR[count]);
|
||||
unsigned int neededLocal = 0;
|
||||
HRESULT hrLocal = GetMethodTableNameImpl(mt, count, mtName != NULL ? (WCHAR *)pwszNameLocal : NULL, pNeeded != NULL ? &neededLocal : NULL);
|
||||
_ASSERTE(hr == hrLocal);
|
||||
|
||||
if (mtName != NULL)
|
||||
{
|
||||
_ASSERTE(0 == u16_strncmp(mtName, (WCHAR *)pwszNameLocal, count));
|
||||
}
|
||||
if (pNeeded != NULL)
|
||||
{
|
||||
_ASSERTE(*pNeeded == neededLocal);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = GetMethodTableNameImpl(mt, count, mtName, pNeeded);
|
||||
}
|
||||
|
||||
SOSDacLeave();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
ClrDataAccess::GetMethodTableNameImpl(CLRDATA_ADDRESS mt, unsigned int count, _Inout_updates_z_(count) WCHAR *mtName, unsigned int *pNeeded)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
PTR_MethodTable pMT = PTR_MethodTable(TO_TADDR(mt));
|
||||
BOOL free = FALSE;
|
||||
|
||||
|
@ -2086,7 +2126,6 @@ ClrDataAccess::GetMethodTableName(CLRDATA_ADDRESS mt, unsigned int count, _Inout
|
|||
}
|
||||
}
|
||||
|
||||
SOSDacLeave();
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// cdac-build-tool can take multiple "-c contract_file" arguments
|
||||
// so to conditionally include contracts, put additional contracts in a separate file
|
||||
{
|
||||
"DacStreams": 1,
|
||||
"Exception": 1,
|
||||
"Loader": 1,
|
||||
"RuntimeTypeSystem": 1,
|
||||
|
|
|
@ -181,6 +181,7 @@ CDAC_TYPE_FIELD(Module, /*pointer*/, Base, cdac_offsets<Module>::Base)
|
|||
CDAC_TYPE_FIELD(Module, /*pointer*/, Flags, cdac_offsets<Module>::Flags)
|
||||
CDAC_TYPE_FIELD(Module, /*pointer*/, LoaderAllocator, cdac_offsets<Module>::LoaderAllocator)
|
||||
CDAC_TYPE_FIELD(Module, /*pointer*/, ThunkHeap, cdac_offsets<Module>::ThunkHeap)
|
||||
CDAC_TYPE_FIELD(Module, /*pointer*/, DynamicMetadata, cdac_offsets<Module>::DynamicMetadata)
|
||||
|
||||
CDAC_TYPE_FIELD(Module, /*pointer*/, FieldDefToDescMap, cdac_offsets<Module>::FieldDefToDescMap)
|
||||
CDAC_TYPE_FIELD(Module, /*pointer*/, ManifestModuleReferencesMap, cdac_offsets<Module>::ManifestModuleReferencesMap)
|
||||
|
@ -202,6 +203,7 @@ CDAC_TYPE_FIELD(MethodTable, /*pointer*/, Module, cdac_offsets<MethodTable>::Mod
|
|||
CDAC_TYPE_FIELD(MethodTable, /*pointer*/, ParentMethodTable, cdac_offsets<MethodTable>::ParentMethodTable)
|
||||
CDAC_TYPE_FIELD(MethodTable, /*uint16*/, NumInterfaces, cdac_offsets<MethodTable>::NumInterfaces)
|
||||
CDAC_TYPE_FIELD(MethodTable, /*uint16*/, NumVirtuals, cdac_offsets<MethodTable>::NumVirtuals)
|
||||
CDAC_TYPE_FIELD(MethodTable, /*pointer*/, PerInstInfo, cdac_offsets<MethodTable>::PerInstInfo)
|
||||
CDAC_TYPE_END(MethodTable)
|
||||
|
||||
CDAC_TYPE_BEGIN(EEClass)
|
||||
|
@ -209,8 +211,47 @@ CDAC_TYPE_INDETERMINATE(EEClass)
|
|||
CDAC_TYPE_FIELD(EEClass, /*pointer*/, MethodTable, cdac_offsets<EEClass>::MethodTable)
|
||||
CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumMethods, cdac_offsets<EEClass>::NumMethods)
|
||||
CDAC_TYPE_FIELD(EEClass, /*uint32*/, CorTypeAttr, cdac_offsets<EEClass>::CorTypeAttr)
|
||||
CDAC_TYPE_FIELD(EEClass, /*uint8*/, InternalCorElementType, cdac_offsets<EEClass>::InternalCorElementType)
|
||||
CDAC_TYPE_END(EEClass)
|
||||
|
||||
CDAC_TYPE_BEGIN(ArrayClass)
|
||||
CDAC_TYPE_INDETERMINATE(ArrayClass)
|
||||
CDAC_TYPE_FIELD(ArrayClass, /*uint8*/, Rank, cdac_offsets<ArrayClass>::Rank)
|
||||
CDAC_TYPE_END(ArrayClass)
|
||||
|
||||
CDAC_TYPE_BEGIN(GenericsDictInfo)
|
||||
CDAC_TYPE_INDETERMINATE(GenericsDictInfo)
|
||||
CDAC_TYPE_FIELD(GenericsDictInfo, /*uint16*/, NumTypeArgs, cdac_offsets<GenericsDictInfo>::NumTypeArgs)
|
||||
CDAC_TYPE_END(GenericsDictInfo)
|
||||
|
||||
CDAC_TYPE_BEGIN(TypeDesc)
|
||||
CDAC_TYPE_INDETERMINATE(TypeDesc)
|
||||
CDAC_TYPE_FIELD(TypeDesc, /*uint32*/, TypeAndFlags, cdac_offsets<TypeDesc>::TypeAndFlags)
|
||||
CDAC_TYPE_END(TypeDesc)
|
||||
|
||||
CDAC_TYPE_BEGIN(ParamTypeDesc)
|
||||
CDAC_TYPE_INDETERMINATE(ParamTypeDesc)
|
||||
CDAC_TYPE_FIELD(ParamTypeDesc, /*pointer*/, TypeArg, cdac_offsets<ParamTypeDesc>::TypeArg)
|
||||
CDAC_TYPE_END(ParamTypeDesc)
|
||||
|
||||
CDAC_TYPE_BEGIN(TypeVarTypeDesc)
|
||||
CDAC_TYPE_INDETERMINATE(TypeVarTypeDesc)
|
||||
CDAC_TYPE_FIELD(TypeVarTypeDesc, /*pointer*/, Module, cdac_offsets<TypeVarTypeDesc>::Module)
|
||||
CDAC_TYPE_FIELD(TypeVarTypeDesc, /*uint32*/, Token, cdac_offsets<TypeVarTypeDesc>::Token)
|
||||
CDAC_TYPE_END(TypeVarTypeDesc)
|
||||
|
||||
CDAC_TYPE_BEGIN(FnPtrTypeDesc)
|
||||
CDAC_TYPE_INDETERMINATE(FnPtrTypeDesc)
|
||||
CDAC_TYPE_FIELD(FnPtrTypeDesc, /*uint32*/, NumArgs, cdac_offsets<FnPtrTypeDesc>::NumArgs)
|
||||
CDAC_TYPE_FIELD(FnPtrTypeDesc, /*uint32*/, CallConv, cdac_offsets<FnPtrTypeDesc>::CallConv)
|
||||
CDAC_TYPE_FIELD(FnPtrTypeDesc, /*uint32*/, RetAndArgTypes, cdac_offsets<FnPtrTypeDesc>::RetAndArgTypes)
|
||||
CDAC_TYPE_END(FnPtrTypeDesc)
|
||||
|
||||
CDAC_TYPE_BEGIN(DynamicMetadata)
|
||||
CDAC_TYPE_FIELD(DynamicMetadata, /*uint32*/, Size, cdac_offsets<DynamicMetadata>::Size)
|
||||
CDAC_TYPE_FIELD(DynamicMetadata, /*inline byte array*/, Data, cdac_offsets<DynamicMetadata>::Data)
|
||||
CDAC_TYPE_END(DynamicMetadata)
|
||||
|
||||
CDAC_TYPES_END()
|
||||
|
||||
CDAC_GLOBALS_BEGIN()
|
||||
|
@ -225,6 +266,8 @@ CDAC_GLOBAL(FeatureEHFunclets, uint8, 0)
|
|||
#endif
|
||||
CDAC_GLOBAL(SOSBreakingChangeVersion, uint8, SOS_BREAKING_CHANGE_VERSION)
|
||||
CDAC_GLOBAL_POINTER(FreeObjectMethodTable, &::g_pFreeObjectMethodTable)
|
||||
CDAC_GLOBAL_POINTER(MiniMetaDataBuffAddress, &::g_MiniMetaDataBuffAddress)
|
||||
CDAC_GLOBAL_POINTER(MiniMetaDataBuffMaxSize, &::g_MiniMetaDataBuffMaxSize)
|
||||
CDAC_GLOBALS_END()
|
||||
|
||||
#undef CDAC_BASELINE
|
||||
|
|
|
@ -344,6 +344,7 @@ Module::Module(Assembly *pAssembly, PEAssembly *pPEAssembly)
|
|||
m_pAssembly = pAssembly;
|
||||
m_pPEAssembly = pPEAssembly;
|
||||
m_dwTransientFlags = CLASSES_FREED;
|
||||
m_pDynamicMetadata = (TADDR)NULL;
|
||||
|
||||
pPEAssembly->AddRef();
|
||||
}
|
||||
|
@ -3748,7 +3749,6 @@ ReflectionModule::ReflectionModule(Assembly *pAssembly, PEAssembly *pPEAssembly)
|
|||
m_pInMemoryWriter = NULL;
|
||||
m_sdataSection = NULL;
|
||||
m_pCeeFileGen = NULL;
|
||||
m_pDynamicMetadata = NULL;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE CreateICeeGen(REFIID riid, void **pCeeGen);
|
||||
|
@ -3806,8 +3806,8 @@ void ReflectionModule::Destruct()
|
|||
|
||||
Module::Destruct();
|
||||
|
||||
delete m_pDynamicMetadata;
|
||||
m_pDynamicMetadata = NULL;
|
||||
delete (uint32_t*)m_pDynamicMetadata;
|
||||
m_pDynamicMetadata = (TADDR)NULL;
|
||||
|
||||
m_CrstLeafLock.Destroy();
|
||||
}
|
||||
|
@ -3923,17 +3923,19 @@ void ReflectionModule::CaptureModuleMetaDataToMemory()
|
|||
IfFailThrow(hr);
|
||||
|
||||
// Operate on local data, and then persist it into the module once we know it's valid.
|
||||
NewHolder<SBuffer> pBuffer(new SBuffer());
|
||||
NewArrayHolder<uint8_t> pBuffer(new uint8_t[numBytes + sizeof(DynamicMetadata)]);
|
||||
_ASSERTE(pBuffer != NULL); // allocation would throw first
|
||||
|
||||
DynamicMetadata *pDynamicMetadata = (DynamicMetadata*)(uint8_t*)pBuffer;
|
||||
|
||||
// ReflectionModule is still in a consistent state, and now we're just operating on local data to
|
||||
// assemble the new metadata buffer. If this fails, then worst case is that metadata does not include
|
||||
// recently generated classes.
|
||||
|
||||
// Caller ensures serialization that guarantees that the metadata doesn't grow underneath us.
|
||||
BYTE * pRawData = pBuffer->OpenRawBuffer(numBytes);
|
||||
BYTE * pRawData = &pDynamicMetadata->Data[0];
|
||||
hr = pEmitter->SaveToMemory(pRawData, numBytes);
|
||||
pBuffer->CloseRawBuffer();
|
||||
pDynamicMetadata->Size = numBytes;
|
||||
|
||||
IfFailThrow(hr);
|
||||
|
||||
|
@ -3941,9 +3943,9 @@ void ReflectionModule::CaptureModuleMetaDataToMemory()
|
|||
{
|
||||
CrstHolder ch(&m_CrstLeafLock);
|
||||
|
||||
delete m_pDynamicMetadata;
|
||||
delete (uint32_t*)m_pDynamicMetadata;
|
||||
|
||||
m_pDynamicMetadata = pBuffer.Extract();
|
||||
m_pDynamicMetadata = (TADDR)pBuffer.Extract();
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -3965,7 +3967,7 @@ void ReflectionModule::CaptureModuleMetaDataToMemory()
|
|||
// Notes:
|
||||
// Only used by the debugger, so only accessible via DAC.
|
||||
// The buffer is updated via code:ReflectionModule.CaptureModuleMetaDataToMemory
|
||||
PTR_SBuffer ReflectionModule::GetDynamicMetadataBuffer() const
|
||||
TADDR ReflectionModule::GetDynamicMetadataBuffer() const
|
||||
{
|
||||
SUPPORTS_DAC;
|
||||
|
||||
|
|
|
@ -91,6 +91,20 @@ typedef DPTR(JITInlineTrackingMap) PTR_JITInlineTrackingMap;
|
|||
|
||||
typedef DPTR(struct LookupMapBase) PTR_LookupMapBase;
|
||||
|
||||
struct DynamicMetadata
|
||||
{
|
||||
uint32_t Size;
|
||||
BYTE Data[0];
|
||||
template<typename T> friend struct ::cdac_offsets;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct cdac_offsets<DynamicMetadata>
|
||||
{
|
||||
static constexpr size_t Size = offsetof(DynamicMetadata, Size);
|
||||
static constexpr size_t Data = offsetof(DynamicMetadata, Data);
|
||||
};
|
||||
|
||||
struct LookupMapBase
|
||||
{
|
||||
DPTR(LookupMapBase) pNext;
|
||||
|
@ -1588,6 +1602,15 @@ private:
|
|||
|
||||
PTR_Assembly *m_NativeMetadataAssemblyRefMap;
|
||||
|
||||
// Buffer of Metadata storage for dynamic modules. May be NULL. This provides a reasonable way for
|
||||
// the debugger to get metadata of dynamic modules from out of process.
|
||||
// A dynamic module will eagerly serialize its metadata to this buffer.
|
||||
// This points at a uint32_t array.
|
||||
// The first uint32_t is the number of bytes in the saved metadata
|
||||
// Starting at the address of the second uint32_t value is the saved metadata itself
|
||||
protected:
|
||||
TADDR m_pDynamicMetadata;
|
||||
|
||||
public:
|
||||
#if !defined(DACCESS_COMPILE)
|
||||
PTR_Assembly GetNativeMetadataAssemblyRefFromCache(DWORD rid)
|
||||
|
@ -1617,6 +1640,7 @@ struct cdac_offsets<Module>
|
|||
static constexpr size_t Flags = offsetof(Module, m_dwTransientFlags);
|
||||
static constexpr size_t LoaderAllocator = offsetof(Module, m_loaderAllocator);
|
||||
static constexpr size_t ThunkHeap = offsetof(Module, m_pThunkHeap);
|
||||
static constexpr size_t DynamicMetadata = offsetof(Module, m_pDynamicMetadata);
|
||||
|
||||
// Lookup map pointers
|
||||
static constexpr size_t FieldDefToDescMap = offsetof(Module, m_FieldDefToDescMap) + offsetof(LookupMap<PTR_FieldDesc>, pTable);
|
||||
|
@ -1647,11 +1671,6 @@ private:
|
|||
// Simple Critical Section used for basic leaf-lock operatons.
|
||||
CrstExplicitInit m_CrstLeafLock;
|
||||
|
||||
// Buffer of Metadata storage for dynamic modules. May be NULL. This provides a reasonable way for
|
||||
// the debugger to get metadata of dynamic modules from out of process.
|
||||
// A dynamic module will eagerly serialize its metadata to this buffer.
|
||||
PTR_SBuffer m_pDynamicMetadata;
|
||||
|
||||
#if !defined DACCESS_COMPILE
|
||||
ReflectionModule(Assembly *pAssembly, PEAssembly *pPEAssembly);
|
||||
#endif // !DACCESS_COMPILE
|
||||
|
@ -1660,7 +1679,7 @@ public:
|
|||
|
||||
#ifdef DACCESS_COMPILE
|
||||
// Accessor to expose m_pDynamicMetadata to debugger.
|
||||
PTR_SBuffer GetDynamicMetadataBuffer() const;
|
||||
TADDR GetDynamicMetadataBuffer() const;
|
||||
#endif
|
||||
|
||||
#if !defined DACCESS_COMPILE
|
||||
|
|
|
@ -1803,6 +1803,7 @@ protected:
|
|||
|
||||
template<> struct cdac_offsets<EEClass>
|
||||
{
|
||||
static constexpr size_t InternalCorElementType = offsetof(EEClass, m_NormType);
|
||||
static constexpr size_t MethodTable = offsetof(EEClass, m_pMethodTable);
|
||||
static constexpr size_t NumMethods = offsetof(EEClass, m_NumMethods);
|
||||
static constexpr size_t CorTypeAttr = offsetof(EEClass, m_dwAttrClass);
|
||||
|
@ -1995,7 +1996,12 @@ public:
|
|||
BOOL fForStubAsIL
|
||||
);
|
||||
|
||||
template<typename T> friend struct ::cdac_offsets;
|
||||
};
|
||||
|
||||
template<> struct cdac_offsets<ArrayClass>
|
||||
{
|
||||
static constexpr size_t Rank = offsetof(ArrayClass, m_rank);
|
||||
};
|
||||
|
||||
inline EEClassLayoutInfo *EEClass::GetLayoutInfo()
|
||||
|
|
|
@ -4832,7 +4832,7 @@ CorElementType MethodTable::GetSignatureCorElementType()
|
|||
// common cases of ELEMENT_TYPE_CLASS and ELEMENT_TYPE_VALUETYPE.
|
||||
CorElementType ret;
|
||||
|
||||
switch (GetFlag(enum_flag_Category_ElementTypeMask))
|
||||
switch (GetFlag(enum_flag_Category_Mask))
|
||||
{
|
||||
case enum_flag_Category_Array:
|
||||
ret = ELEMENT_TYPE_ARRAY;
|
||||
|
@ -4843,17 +4843,13 @@ CorElementType MethodTable::GetSignatureCorElementType()
|
|||
break;
|
||||
|
||||
case enum_flag_Category_ValueType:
|
||||
case enum_flag_Category_Nullable:
|
||||
case enum_flag_Category_PrimitiveValueType:
|
||||
ret = ELEMENT_TYPE_VALUETYPE;
|
||||
break;
|
||||
|
||||
case enum_flag_Category_PrimitiveValueType:
|
||||
//
|
||||
// This is the only difference from MethodTable::GetInternalCorElementType()
|
||||
//
|
||||
if (IsTruePrimitive())
|
||||
ret = GetClass()->GetInternalCorElementType();
|
||||
else
|
||||
ret = ELEMENT_TYPE_VALUETYPE;
|
||||
case enum_flag_Category_TruePrimitive:
|
||||
ret = GetClass()->GetInternalCorElementType();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -293,9 +293,15 @@ struct GenericsDictInfo
|
|||
|
||||
// Number of type parameters (NOT including those of superclasses).
|
||||
WORD m_wNumTyPars;
|
||||
template<typename T> friend struct ::cdac_offsets;
|
||||
}; // struct GenericsDictInfo
|
||||
typedef DPTR(GenericsDictInfo) PTR_GenericsDictInfo;
|
||||
|
||||
template<>
|
||||
struct cdac_offsets<GenericsDictInfo>
|
||||
{
|
||||
static constexpr size_t NumTypeArgs = offsetof(GenericsDictInfo, m_wNumTyPars);
|
||||
};
|
||||
|
||||
// These various statics structures exist directly before the MethodTableAuxiliaryData
|
||||
|
||||
|
@ -3943,6 +3949,7 @@ template<> struct cdac_offsets<MethodTable>
|
|||
static constexpr size_t ParentMethodTable = offsetof(MethodTable, m_pParentMethodTable);
|
||||
static constexpr size_t NumInterfaces = offsetof(MethodTable, m_wNumInterfaces);
|
||||
static constexpr size_t NumVirtuals = offsetof(MethodTable, m_wNumVirtuals);
|
||||
static constexpr size_t PerInstInfo = offsetof(MethodTable, m_pPerInstInfo);
|
||||
};
|
||||
|
||||
#ifndef CROSSBITNESS_COMPILE
|
||||
|
|
|
@ -204,8 +204,14 @@ public:
|
|||
|
||||
// internal RuntimeType object handle
|
||||
RUNTIMETYPEHANDLE m_hExposedClassObject;
|
||||
template<typename T> friend struct ::cdac_offsets;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct cdac_offsets<TypeDesc>
|
||||
{
|
||||
static constexpr size_t TypeAndFlags = offsetof(TypeDesc, m_typeAndFlags);
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
// This variant is used for parameterized types that have exactly one argument
|
||||
|
@ -263,6 +269,13 @@ protected:
|
|||
|
||||
// The type that is being modified
|
||||
TypeHandle m_Arg;
|
||||
template<typename T> friend struct ::cdac_offsets;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct cdac_offsets<ParamTypeDesc>
|
||||
{
|
||||
static constexpr size_t TypeArg = offsetof(ParamTypeDesc, m_Arg);
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
@ -381,6 +394,15 @@ protected:
|
|||
|
||||
// index within declaring type or method, numbered from zero
|
||||
unsigned int m_index;
|
||||
|
||||
template<typename T> friend struct ::cdac_offsets;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct cdac_offsets<TypeVarTypeDesc>
|
||||
{
|
||||
static constexpr size_t Module = offsetof(TypeVarTypeDesc, m_pModule);
|
||||
static constexpr size_t Token = offsetof(TypeVarTypeDesc, m_token);
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
@ -467,6 +489,16 @@ protected:
|
|||
|
||||
// Return type first, then argument types
|
||||
TypeHandle m_RetAndArgTypes[1];
|
||||
|
||||
template<typename T> friend struct ::cdac_offsets;
|
||||
}; // class FnPtrTypeDesc
|
||||
|
||||
template<>
|
||||
struct cdac_offsets<FnPtrTypeDesc>
|
||||
{
|
||||
static constexpr size_t NumArgs = offsetof(FnPtrTypeDesc, m_NumArgs);
|
||||
static constexpr size_t RetAndArgTypes = offsetof(FnPtrTypeDesc, m_RetAndArgTypes);
|
||||
static constexpr size_t CallConv = offsetof(FnPtrTypeDesc, m_CallConv);
|
||||
};
|
||||
|
||||
#endif // TYPEDESC_H
|
||||
|
|
|
@ -17,5 +17,8 @@ internal static class Constants
|
|||
internal const string SOSBreakingChangeVersion = nameof(SOSBreakingChangeVersion);
|
||||
|
||||
internal const string FreeObjectMethodTable = nameof(FreeObjectMethodTable);
|
||||
|
||||
internal const string MiniMetaDataBuffAddress = nameof(MiniMetaDataBuffAddress);
|
||||
internal const string MiniMetaDataBuffMaxSize = nameof(MiniMetaDataBuffMaxSize);
|
||||
}
|
||||
}
|
||||
|
|
30
src/native/managed/cdacreader/src/Contracts/DacStreams.cs
Normal file
30
src/native/managed/cdacreader/src/Contracts/DacStreams.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// 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.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
|
||||
|
||||
internal interface IDacStreams : IContract
|
||||
{
|
||||
static string IContract.Name { get; } = nameof(DacStreams);
|
||||
static IContract IContract.Create(Target target, int version)
|
||||
{
|
||||
return version switch
|
||||
{
|
||||
1 => new DacStreams_1(target),
|
||||
_ => default(DacStreams),
|
||||
};
|
||||
}
|
||||
|
||||
public virtual string? StringFromEEAddress(TargetPointer address) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal readonly struct DacStreams : IDacStreams
|
||||
{
|
||||
// Everything throws NotImplementedException
|
||||
}
|
127
src/native/managed/cdacreader/src/Contracts/DacStreams_1.cs
Normal file
127
src/native/managed/cdacreader/src/Contracts/DacStreams_1.cs
Normal file
|
@ -0,0 +1,127 @@
|
|||
// 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.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Diagnostics.DataContractReader.Data;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
|
||||
|
||||
internal class DacStreams_1 : IDacStreams
|
||||
{
|
||||
private readonly Target _target;
|
||||
|
||||
private const uint MiniMetadataSignature = 0x6d727473;
|
||||
private const uint EENameStreamSignature = 0x614e4545;
|
||||
|
||||
private const uint MiniMetaDataStreamsHeaderSize = 12;
|
||||
private const uint MiniMetadataStream_MiniMetadataSignature_Offset = 0;
|
||||
private const uint MiniMetadataStream_TotalSize_Offset = 4;
|
||||
private const uint MiniMetadataStream_CountOfStreams_Offset = 8;
|
||||
|
||||
private const uint EENameStreamHeaderSize = 8;
|
||||
private const uint EENameStream_EENameStreamSignature_Offset = 0;
|
||||
private const uint EENameStream_CountOfNames_Offset = 4;
|
||||
|
||||
internal DacStreams_1(Target target)
|
||||
{
|
||||
_target = target;
|
||||
}
|
||||
|
||||
public virtual string? StringFromEEAddress(TargetPointer address)
|
||||
{
|
||||
// We use the data subsystem to handle caching results from processing this data
|
||||
var dictionary = _target.ProcessedData.GetOrAdd<DacStreams_1_Data>(0).EEObjectToString;
|
||||
|
||||
dictionary.TryGetValue(address, out string? result);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal class DacStreams_1_Data : IData<DacStreams_1_Data>
|
||||
{
|
||||
static DacStreams_1_Data IData<DacStreams_1_Data>.Create(Target target, TargetPointer address) => new DacStreams_1_Data(target);
|
||||
|
||||
public DacStreams_1_Data(Target target)
|
||||
{
|
||||
EEObjectToString = GetEEAddressToStringMap(target);
|
||||
}
|
||||
|
||||
public readonly Dictionary<TargetPointer, string> EEObjectToString;
|
||||
|
||||
internal static Dictionary<TargetPointer, string> GetEEAddressToStringMap(Target target)
|
||||
{
|
||||
TargetPointer miniMetaDataBuffAddress = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.MiniMetaDataBuffAddress));
|
||||
uint miniMetaDataBuffMaxSize = target.Read<uint>(target.ReadGlobalPointer(Constants.Globals.MiniMetaDataBuffMaxSize));
|
||||
ulong miniMetaDataBuffEnd = miniMetaDataBuffAddress + miniMetaDataBuffMaxSize;
|
||||
|
||||
Dictionary<TargetPointer, string> stringToAddress = new();
|
||||
if (miniMetaDataBuffMaxSize < 20)
|
||||
{
|
||||
// buffer isn't long enough to hold required headers
|
||||
return stringToAddress;
|
||||
}
|
||||
|
||||
if (target.Read<uint>(miniMetaDataBuffAddress + MiniMetadataStream_MiniMetadataSignature_Offset) != MiniMetadataSignature)
|
||||
{
|
||||
// Magic number is incorrect
|
||||
return stringToAddress;
|
||||
}
|
||||
|
||||
|
||||
uint totalSize = target.Read<uint>(miniMetaDataBuffAddress + MiniMetadataStream_TotalSize_Offset);
|
||||
if (totalSize > miniMetaDataBuffMaxSize)
|
||||
{
|
||||
// totalSize is inconsistent with miniMetaDataBuffMaxSize
|
||||
return stringToAddress;
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[totalSize];
|
||||
ReadOnlySpan<byte> miniMdBuffer = bytes.AsSpan();
|
||||
target.ReadBuffer(miniMetaDataBuffAddress, bytes);
|
||||
uint countStreams = target.Read<uint>(miniMetaDataBuffAddress + MiniMetadataStream_CountOfStreams_Offset);
|
||||
if (countStreams != 1)
|
||||
{
|
||||
// This implementation is only aware of 1 possible stream type, so only 1 can exist
|
||||
return stringToAddress;
|
||||
}
|
||||
ulong eeNameStreamAddress = miniMetaDataBuffAddress + MiniMetaDataStreamsHeaderSize;
|
||||
uint eeNameSig = target.Read<uint>(eeNameStreamAddress + EENameStream_EENameStreamSignature_Offset);
|
||||
if (eeNameSig != EENameStreamSignature)
|
||||
{
|
||||
// name of first stream is not 0x614e4545 == "EENa"
|
||||
return stringToAddress;
|
||||
}
|
||||
uint countNames = target.Read<uint>(eeNameStreamAddress + EENameStream_CountOfNames_Offset);
|
||||
|
||||
ulong currentAddress = eeNameStreamAddress + EENameStreamHeaderSize;
|
||||
|
||||
for (int i = 0; i < countNames; i++)
|
||||
{
|
||||
if (currentAddress >= miniMetaDataBuffEnd)
|
||||
break;
|
||||
TargetPointer eeObjectPointer = target.ReadPointer(currentAddress);
|
||||
currentAddress += (uint)target.PointerSize;
|
||||
int stringLen = miniMdBuffer.Slice((int)(currentAddress - miniMetaDataBuffAddress)).IndexOf((byte)0);
|
||||
if (stringLen == -1)
|
||||
break;
|
||||
|
||||
try
|
||||
{
|
||||
string name = Encoding.UTF8.GetString(miniMdBuffer.Slice((int)(currentAddress - miniMetaDataBuffAddress), stringLen));
|
||||
stringToAddress.Add(eeObjectPointer, name);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Tolerate malformed strings without causing all lookups to fail
|
||||
}
|
||||
|
||||
currentAddress += (uint)stringLen + 1;
|
||||
}
|
||||
|
||||
return stringToAddress;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,6 +31,75 @@ internal record struct ModuleLookupTables(
|
|||
TargetPointer TypeDefToMethodTable,
|
||||
TargetPointer TypeRefToMethodTable);
|
||||
|
||||
internal struct EcmaMetadataSchema
|
||||
{
|
||||
public EcmaMetadataSchema(string metadataVersion, bool largeStringHeap, bool largeBlobHeap, bool largeGuidHeap, int[] rowCount, bool[] isSorted, bool variableSizedColumnsAre4BytesLong)
|
||||
{
|
||||
MetadataVersion = metadataVersion;
|
||||
LargeStringHeap = largeStringHeap;
|
||||
LargeBlobHeap = largeBlobHeap;
|
||||
LargeGuidHeap = largeGuidHeap;
|
||||
|
||||
_rowCount = rowCount;
|
||||
_isSorted = isSorted;
|
||||
|
||||
VariableSizedColumnsAreAll4BytesLong = variableSizedColumnsAre4BytesLong;
|
||||
}
|
||||
|
||||
public readonly string MetadataVersion;
|
||||
|
||||
public readonly bool LargeStringHeap;
|
||||
public readonly bool LargeBlobHeap;
|
||||
public readonly bool LargeGuidHeap;
|
||||
|
||||
// Table data, these structures hold MetadataTable.Count entries
|
||||
private readonly int[] _rowCount;
|
||||
public readonly ReadOnlySpan<int> RowCount => _rowCount;
|
||||
|
||||
private readonly bool[] _isSorted;
|
||||
public readonly ReadOnlySpan<bool> IsSorted => _isSorted;
|
||||
|
||||
// In certain scenarios the size of the tables is forced to be the maximum size
|
||||
// Otherwise the size of columns should be computed based on RowSize/the various heap flags
|
||||
public readonly bool VariableSizedColumnsAreAll4BytesLong;
|
||||
}
|
||||
|
||||
internal class TargetEcmaMetadata
|
||||
{
|
||||
public TargetEcmaMetadata(EcmaMetadataSchema schema,
|
||||
TargetSpan[] tables,
|
||||
TargetSpan stringHeap,
|
||||
TargetSpan userStringHeap,
|
||||
TargetSpan blobHeap,
|
||||
TargetSpan guidHeap)
|
||||
{
|
||||
Schema = schema;
|
||||
_tables = tables;
|
||||
StringHeap = stringHeap;
|
||||
UserStringHeap = userStringHeap;
|
||||
BlobHeap = blobHeap;
|
||||
GuidHeap = guidHeap;
|
||||
}
|
||||
|
||||
public EcmaMetadataSchema Schema { get; init; }
|
||||
|
||||
private TargetSpan[] _tables;
|
||||
public ReadOnlySpan<TargetSpan> Tables => _tables;
|
||||
public TargetSpan StringHeap { get; init; }
|
||||
public TargetSpan UserStringHeap { get; init; }
|
||||
public TargetSpan BlobHeap { get; init; }
|
||||
public TargetSpan GuidHeap { get; init; }
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum AvailableMetadataType
|
||||
{
|
||||
None = 0,
|
||||
ReadOnly = 1,
|
||||
ReadWriteSavedCopy = 2,
|
||||
ReadWrite = 4
|
||||
}
|
||||
|
||||
internal interface ILoader : IContract
|
||||
{
|
||||
static string IContract.Name => nameof(Loader);
|
||||
|
@ -51,8 +120,15 @@ internal interface ILoader : IContract
|
|||
public virtual TargetPointer GetThunkHeap(ModuleHandle handle) => throw new NotImplementedException();
|
||||
|
||||
public virtual TargetPointer GetILBase(ModuleHandle handle) => throw new NotImplementedException();
|
||||
|
||||
public virtual TargetPointer GetMetadataAddress(ModuleHandle handle, out ulong size) => throw new NotImplementedException();
|
||||
|
||||
public virtual AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle) => throw new NotImplementedException();
|
||||
|
||||
public virtual TargetPointer GetReadWriteSavedMetadataAddress(ModuleHandle handle, out ulong size) => throw new NotImplementedException();
|
||||
|
||||
public virtual TargetEcmaMetadata GetReadWriteMetadata(ModuleHandle handle) => throw new NotImplementedException();
|
||||
|
||||
public virtual ModuleLookupTables GetLookupTables(ModuleHandle handle) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,33 @@ internal readonly struct Loader_1 : ILoader
|
|||
return module.GetLoadedMetadata(out size);
|
||||
}
|
||||
|
||||
AvailableMetadataType ILoader.GetAvailableMetadataType(ModuleHandle handle)
|
||||
{
|
||||
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
|
||||
|
||||
AvailableMetadataType flags = AvailableMetadataType.None;
|
||||
|
||||
if (module.DynamicMetadata != TargetPointer.Null)
|
||||
flags |= AvailableMetadataType.ReadWriteSavedCopy;
|
||||
else
|
||||
flags |= AvailableMetadataType.ReadOnly;
|
||||
|
||||
// TODO(cdac) implement direct reading of unsaved ReadWrite metadata
|
||||
return flags;
|
||||
}
|
||||
|
||||
TargetPointer ILoader.GetReadWriteSavedMetadataAddress(ModuleHandle handle, out ulong size)
|
||||
{
|
||||
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
|
||||
Data.DynamicMetadata dynamicMetadata = _target.ProcessedData.GetOrAdd<Data.DynamicMetadata>(module.DynamicMetadata);
|
||||
TargetPointer result = dynamicMetadata.Data;
|
||||
size = dynamicMetadata.Size;
|
||||
return result;
|
||||
}
|
||||
|
||||
TargetEcmaMetadata ILoader.GetReadWriteMetadata(ModuleHandle handle) => throw new NotImplementedException();
|
||||
|
||||
|
||||
ModuleLookupTables ILoader.GetLookupTables(ModuleHandle handle)
|
||||
{
|
||||
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
|
||||
|
|
|
@ -22,6 +22,7 @@ internal sealed class Registry
|
|||
public ILoader Loader => GetContract<ILoader>();
|
||||
public IThread Thread => GetContract<IThread>();
|
||||
public IRuntimeTypeSystem RuntimeTypeSystem => GetContract<IRuntimeTypeSystem>();
|
||||
public IDacStreams DacStreams => GetContract<IDacStreams>();
|
||||
|
||||
private T GetContract<T>() where T : IContract
|
||||
{
|
||||
|
|
|
@ -6,15 +6,53 @@ using System.Reflection.Metadata.Ecma335;
|
|||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
|
||||
|
||||
// an opaque handle to a method table. See IMetadata.GetMethodTableData
|
||||
internal readonly struct MethodTableHandle
|
||||
// an opaque handle to a type handle. See IMetadata.GetMethodTableData
|
||||
internal readonly struct TypeHandle
|
||||
{
|
||||
internal MethodTableHandle(TargetPointer address)
|
||||
internal TypeHandle(TargetPointer address)
|
||||
{
|
||||
Address = address;
|
||||
}
|
||||
|
||||
internal TargetPointer Address { get; }
|
||||
|
||||
internal bool IsNull => Address == 0;
|
||||
}
|
||||
|
||||
internal enum CorElementType
|
||||
{
|
||||
Void = 1,
|
||||
Boolean = 2,
|
||||
Char = 3,
|
||||
I1 = 4,
|
||||
U1 = 5,
|
||||
I2 = 6,
|
||||
U2 = 7,
|
||||
I4 = 8,
|
||||
U4 = 9,
|
||||
I8 = 0xa,
|
||||
U8 = 0xb,
|
||||
R4 = 0xc,
|
||||
R8 = 0xd,
|
||||
String = 0xe,
|
||||
Ptr = 0xf,
|
||||
Byref = 0x10,
|
||||
ValueType = 0x11,
|
||||
Class = 0x12,
|
||||
Var = 0x13,
|
||||
Array = 0x14,
|
||||
GenericInst = 0x15,
|
||||
TypedByRef = 0x16,
|
||||
I = 0x18,
|
||||
U = 0x19,
|
||||
FnPtr = 0x1b,
|
||||
Object = 0x1c,
|
||||
SzArray = 0x1d,
|
||||
MVar = 0x1e,
|
||||
CModReqd = 0x1f,
|
||||
CModOpt = 0x20,
|
||||
Internal = 0x21,
|
||||
Sentinel = 0x41,
|
||||
}
|
||||
|
||||
internal interface IRuntimeTypeSystem : IContract
|
||||
|
@ -31,34 +69,50 @@ internal interface IRuntimeTypeSystem : IContract
|
|||
};
|
||||
}
|
||||
|
||||
#region MethodTable inspection APIs
|
||||
public virtual MethodTableHandle GetMethodTableHandle(TargetPointer targetPointer) => throw new NotImplementedException();
|
||||
|
||||
public virtual TargetPointer GetModule(MethodTableHandle methodTable) => throw new NotImplementedException();
|
||||
#region TypeHandle inspection APIs
|
||||
public virtual TypeHandle GetTypeHandle(TargetPointer address) => throw new NotImplementedException();
|
||||
public virtual TargetPointer GetModule(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
// A canonical method table is either the MethodTable itself, or in the case of a generic instantiation, it is the
|
||||
// MethodTable of the prototypical instance.
|
||||
public virtual TargetPointer GetCanonicalMethodTable(MethodTableHandle methodTable) => throw new NotImplementedException();
|
||||
public virtual TargetPointer GetParentMethodTable(MethodTableHandle methodTable) => throw new NotImplementedException();
|
||||
public virtual TargetPointer GetCanonicalMethodTable(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
public virtual TargetPointer GetParentMethodTable(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
|
||||
public virtual uint GetBaseSize(MethodTableHandle methodTable) => throw new NotImplementedException();
|
||||
public virtual uint GetBaseSize(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
// The component size is only available for strings and arrays. It is the size of the element type of the array, or the size of an ECMA 335 character (2 bytes)
|
||||
public virtual uint GetComponentSize(MethodTableHandle methodTable) => throw new NotImplementedException();
|
||||
public virtual uint GetComponentSize(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
|
||||
// True if the MethodTable is the sentinel value associated with unallocated space in the managed heap
|
||||
public virtual bool IsFreeObjectMethodTable(MethodTableHandle methodTable) => throw new NotImplementedException();
|
||||
public virtual bool IsString(MethodTableHandle methodTable) => throw new NotImplementedException();
|
||||
public virtual bool IsFreeObjectMethodTable(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
public virtual bool IsString(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
// True if the MethodTable represents a type that contains managed references
|
||||
public virtual bool ContainsGCPointers(MethodTableHandle methodTable) => throw new NotImplementedException();
|
||||
public virtual bool IsDynamicStatics(MethodTableHandle methodTable) => throw new NotImplementedException();
|
||||
public virtual ushort GetNumMethods(MethodTableHandle methodTable) => throw new NotImplementedException();
|
||||
public virtual ushort GetNumInterfaces(MethodTableHandle methodTable) => throw new NotImplementedException();
|
||||
public virtual bool ContainsGCPointers(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
public virtual bool IsDynamicStatics(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
public virtual ushort GetNumMethods(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
public virtual ushort GetNumInterfaces(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
|
||||
// Returns an ECMA-335 TypeDef table token for this type, or for its generic type definition if it is a generic instantiation
|
||||
public virtual uint GetTypeDefToken(MethodTableHandle methodTable) => throw new NotImplementedException();
|
||||
public virtual uint GetTypeDefToken(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
// Returns the ECMA 335 TypeDef table Flags value (a bitmask of TypeAttributes) for this type,
|
||||
// or for its generic type definition if it is a generic instantiation
|
||||
public virtual uint GetTypeDefTypeAttributes(MethodTableHandle methodTable) => throw new NotImplementedException();
|
||||
#endregion MethodTable inspection APIs
|
||||
public virtual uint GetTypeDefTypeAttributes(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
|
||||
public virtual ReadOnlySpan<TypeHandle> GetInstantiation(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
public virtual bool IsGenericTypeDefinition(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
|
||||
public virtual bool HasTypeParam(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
|
||||
// Element type of the type. NOTE: this drops the CorElementType.GenericInst, and CorElementType.String is returned as CorElementType.Class.
|
||||
// If this returns CorElementType.ValueType it may be a normal valuetype or a "NATIVE" valuetype used to represent an interop view on a structure
|
||||
// HasTypeParam will return true for cases where this is the interop view
|
||||
public virtual CorElementType GetSignatureCorElementType(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
|
||||
// return true if the TypeHandle represents an array, and set the rank to either 0 (if the type is not an array), or the rank number if it is.
|
||||
public virtual bool IsArray(TypeHandle typeHandle, out uint rank) => throw new NotImplementedException();
|
||||
public virtual TypeHandle GetTypeParam(TypeHandle typeHandle) => throw new NotImplementedException();
|
||||
public virtual bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token) => throw new NotImplementedException();
|
||||
public virtual bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv) => throw new NotImplementedException();
|
||||
// Returns null if the TypeHandle is not a class/struct/generic variable
|
||||
#endregion TypeHandle inspection APIs
|
||||
}
|
||||
|
||||
internal struct RuntimeTypeSystem : IRuntimeTypeSystem
|
||||
|
|
|
@ -14,6 +14,7 @@ internal partial struct RuntimeTypeSystem_1
|
|||
{
|
||||
GenericsMask = 0x00000030,
|
||||
GenericsMask_NonGeneric = 0x00000000, // no instantiation
|
||||
GenericsMask_TypicalInstantiation = 0x00000030, // the type instantiated at its formal parameters, e.g. List<T>
|
||||
|
||||
StringArrayValues =
|
||||
GenericsMask_NonGeneric |
|
||||
|
@ -26,7 +27,13 @@ internal partial struct RuntimeTypeSystem_1
|
|||
{
|
||||
Category_Mask = 0x000F0000,
|
||||
Category_Array = 0x00080000,
|
||||
Category_IfArrayThenSzArray = 0x00020000,
|
||||
Category_Array_Mask = 0x000C0000,
|
||||
Category_ElementType_Mask = 0x000E0000,
|
||||
Category_ValueType = 0x00040000,
|
||||
Category_Nullable = 0x00050000,
|
||||
Category_PrimitiveValueType = 0x00060000,
|
||||
Category_TruePrimitive = 0x00070000,
|
||||
Category_Interface = 0x000C0000,
|
||||
ContainsGCPointers = 0x01000000,
|
||||
HasComponentSize = 0x80000000, // This is set if lower 16 bits is used for the component size,
|
||||
|
@ -83,5 +90,6 @@ internal partial struct RuntimeTypeSystem_1
|
|||
public bool HasInstantiation => !TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_NonGeneric);
|
||||
public bool ContainsGCPointers => GetFlag(WFLAGS_HIGH.ContainsGCPointers) != 0;
|
||||
public bool IsDynamicStatics => GetFlag(WFLAGS2_ENUM.DynamicStatics) != 0;
|
||||
public bool IsGenericTypeDefinition => TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_TypicalInstantiation);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using Microsoft.Diagnostics.DataContractReader.Data;
|
||||
using Microsoft.Diagnostics.DataContractReader.Contracts.RuntimeTypeSystem_1_NS;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
|
||||
|
||||
|
||||
|
||||
internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
|
||||
{
|
||||
private readonly Target _target;
|
||||
|
@ -27,6 +29,7 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
|
|||
internal TargetPointer ParentMethodTable { get; }
|
||||
internal TargetPointer Module { get; }
|
||||
internal TargetPointer EEClassOrCanonMT { get; }
|
||||
internal TargetPointer PerInstInfo { get; }
|
||||
internal MethodTable(Data.MethodTable data)
|
||||
{
|
||||
Flags = new MethodTableFlags
|
||||
|
@ -40,6 +43,7 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
|
|||
EEClassOrCanonMT = data.EEClassOrCanonMT;
|
||||
Module = data.Module;
|
||||
ParentMethodTable = data.ParentMethodTable;
|
||||
PerInstInfo = data.PerInstInfo;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +57,16 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
|
|||
Mask = 1,
|
||||
}
|
||||
|
||||
// Low order bits of TypeHandle address.
|
||||
// If the low bits contain a 2, then it is a TypeDesc
|
||||
[Flags]
|
||||
internal enum TypeHandleBits
|
||||
{
|
||||
MethodTable = 0,
|
||||
TypeDesc = 2,
|
||||
ValidMask = 2,
|
||||
}
|
||||
|
||||
internal RuntimeTypeSystem_1(Target target, TargetPointer freeObjectMethodTablePointer)
|
||||
{
|
||||
_target = target;
|
||||
|
@ -61,21 +75,37 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
|
|||
|
||||
internal TargetPointer FreeObjectMethodTablePointer => _freeObjectMethodTablePointer;
|
||||
|
||||
|
||||
public MethodTableHandle GetMethodTableHandle(TargetPointer methodTablePointer)
|
||||
public TypeHandle GetTypeHandle(TargetPointer typeHandlePointer)
|
||||
{
|
||||
// if we already validated this address, return a handle
|
||||
if (_methodTables.ContainsKey(methodTablePointer))
|
||||
TypeHandleBits addressLowBits = (TypeHandleBits)((ulong)typeHandlePointer & ((ulong)_target.PointerSize - 1));
|
||||
|
||||
if ((addressLowBits != TypeHandleBits.MethodTable) && (addressLowBits != TypeHandleBits.TypeDesc))
|
||||
{
|
||||
return new MethodTableHandle(methodTablePointer);
|
||||
throw new InvalidOperationException("Invalid type handle pointer");
|
||||
}
|
||||
|
||||
// if we already validated this address, return a handle
|
||||
if (_methodTables.ContainsKey(typeHandlePointer))
|
||||
{
|
||||
return new TypeHandle(typeHandlePointer);
|
||||
}
|
||||
|
||||
// Check for a TypeDesc
|
||||
if (addressLowBits == TypeHandleBits.TypeDesc)
|
||||
{
|
||||
// This is a TypeDesc
|
||||
return new TypeHandle(typeHandlePointer);
|
||||
}
|
||||
|
||||
TargetPointer methodTablePointer = typeHandlePointer;
|
||||
|
||||
// Check if we cached the underlying data already
|
||||
if (_target.ProcessedData.TryGet(methodTablePointer, out Data.MethodTable? methodTableData))
|
||||
{
|
||||
// we already cached the data, we must have validated the address, create the representation struct for our use
|
||||
MethodTable trustedMethodTable = new MethodTable(methodTableData);
|
||||
_ = _methodTables.TryAdd(methodTablePointer, trustedMethodTable);
|
||||
return new MethodTableHandle(methodTablePointer);
|
||||
return new TypeHandle(methodTablePointer);
|
||||
}
|
||||
|
||||
// If it's the free object method table, we trust it to be valid
|
||||
|
@ -84,7 +114,7 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
|
|||
Data.MethodTable freeObjectMethodTableData = _target.ProcessedData.GetOrAdd<Data.MethodTable>(methodTablePointer);
|
||||
MethodTable trustedMethodTable = new MethodTable(freeObjectMethodTableData);
|
||||
_ = _methodTables.TryAdd(methodTablePointer, trustedMethodTable);
|
||||
return new MethodTableHandle(methodTablePointer);
|
||||
return new TypeHandle(methodTablePointer);
|
||||
}
|
||||
|
||||
// Otherwse, get ready to validate
|
||||
|
@ -98,24 +128,23 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
|
|||
Data.MethodTable trustedMethodTableData = _target.ProcessedData.GetOrAdd<Data.MethodTable>(methodTablePointer);
|
||||
MethodTable trustedMethodTableF = new MethodTable(trustedMethodTableData);
|
||||
_ = _methodTables.TryAdd(methodTablePointer, trustedMethodTableF);
|
||||
return new MethodTableHandle(methodTablePointer);
|
||||
return new TypeHandle(methodTablePointer);
|
||||
}
|
||||
|
||||
public uint GetBaseSize(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (uint)0 : _methodTables[typeHandle.Address].Flags.BaseSize;
|
||||
|
||||
public uint GetBaseSize(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.BaseSize;
|
||||
public uint GetComponentSize(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (uint)0 : _methodTables[typeHandle.Address].Flags.ComponentSize;
|
||||
|
||||
public uint GetComponentSize(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.ComponentSize;
|
||||
|
||||
private TargetPointer GetClassPointer(MethodTableHandle methodTableHandle)
|
||||
private TargetPointer GetClassPointer(TypeHandle typeHandle)
|
||||
{
|
||||
MethodTable methodTable = _methodTables[methodTableHandle.Address];
|
||||
MethodTable methodTable = _methodTables[typeHandle.Address];
|
||||
switch (GetEEClassOrCanonMTBits(methodTable.EEClassOrCanonMT))
|
||||
{
|
||||
case EEClassOrCanonMTBits.EEClass:
|
||||
return methodTable.EEClassOrCanonMT;
|
||||
case EEClassOrCanonMTBits.CanonMT:
|
||||
TargetPointer canonMTPtr = new TargetPointer((ulong)methodTable.EEClassOrCanonMT & ~(ulong)RuntimeTypeSystem_1.EEClassOrCanonMTBits.Mask);
|
||||
MethodTableHandle canonMTHandle = GetMethodTableHandle(canonMTPtr);
|
||||
TypeHandle canonMTHandle = GetTypeHandle(canonMTPtr);
|
||||
MethodTable canonMT = _methodTables[canonMTHandle.Address];
|
||||
return canonMT.EEClassOrCanonMT; // canonical method table EEClassOrCanonMT is always EEClass
|
||||
default:
|
||||
|
@ -124,34 +153,268 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
|
|||
}
|
||||
|
||||
// only called on validated method tables, so we don't need to re-validate the EEClass
|
||||
private Data.EEClass GetClassData(MethodTableHandle methodTableHandle)
|
||||
private Data.EEClass GetClassData(TypeHandle typeHandle)
|
||||
{
|
||||
TargetPointer clsPtr = GetClassPointer(methodTableHandle);
|
||||
TargetPointer clsPtr = GetClassPointer(typeHandle);
|
||||
return _target.ProcessedData.GetOrAdd<Data.EEClass>(clsPtr);
|
||||
}
|
||||
|
||||
public TargetPointer GetCanonicalMethodTable(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).MethodTable;
|
||||
public TargetPointer GetCanonicalMethodTable(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : GetClassData(typeHandle).MethodTable;
|
||||
|
||||
public TargetPointer GetModule(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Module;
|
||||
public TargetPointer GetParentMethodTable(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].ParentMethodTable;
|
||||
|
||||
public bool IsFreeObjectMethodTable(MethodTableHandle methodTableHandle) => FreeObjectMethodTablePointer == methodTableHandle.Address;
|
||||
|
||||
public bool IsString(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.IsString;
|
||||
public bool ContainsGCPointers(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.ContainsGCPointers;
|
||||
|
||||
public uint GetTypeDefToken(MethodTableHandle methodTableHandle)
|
||||
public TargetPointer GetModule(TypeHandle typeHandle)
|
||||
{
|
||||
MethodTable methodTable = _methodTables[methodTableHandle.Address];
|
||||
if (typeHandle.IsMethodTable())
|
||||
{
|
||||
return _methodTables[typeHandle.Address].Module;
|
||||
}
|
||||
else if (typeHandle.IsTypeDesc())
|
||||
{
|
||||
if (HasTypeParam(typeHandle))
|
||||
{
|
||||
return GetModule(GetTypeParam(typeHandle));
|
||||
}
|
||||
else if (IsGenericVariable(typeHandle, out TargetPointer genericParamModule, out _))
|
||||
{
|
||||
return genericParamModule;
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(IsFunctionPointer(typeHandle, out _, out _));
|
||||
return TargetPointer.Null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return TargetPointer.Null;
|
||||
}
|
||||
}
|
||||
|
||||
public TargetPointer GetParentMethodTable(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : _methodTables[typeHandle.Address].ParentMethodTable;
|
||||
|
||||
public bool IsFreeObjectMethodTable(TypeHandle typeHandle) => FreeObjectMethodTablePointer == typeHandle.Address;
|
||||
|
||||
public bool IsString(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.IsString;
|
||||
public bool ContainsGCPointers(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.ContainsGCPointers;
|
||||
|
||||
public uint GetTypeDefToken(TypeHandle typeHandle)
|
||||
{
|
||||
if (!typeHandle.IsMethodTable())
|
||||
return 0;
|
||||
MethodTable methodTable = _methodTables[typeHandle.Address];
|
||||
return (uint)(methodTable.Flags.GetTypeDefRid() | ((int)TableIndex.TypeDef << 24));
|
||||
}
|
||||
|
||||
public ushort GetNumMethods(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).NumMethods;
|
||||
public ushort GetNumMethods(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumMethods;
|
||||
|
||||
public ushort GetNumInterfaces(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].NumInterfaces;
|
||||
public ushort GetNumInterfaces(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : _methodTables[typeHandle.Address].NumInterfaces;
|
||||
|
||||
public uint GetTypeDefTypeAttributes(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).CorTypeAttr;
|
||||
public uint GetTypeDefTypeAttributes(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (uint)0 : GetClassData(typeHandle).CorTypeAttr;
|
||||
|
||||
public bool IsDynamicStatics(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.IsDynamicStatics;
|
||||
public bool IsDynamicStatics(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.IsDynamicStatics;
|
||||
|
||||
public ReadOnlySpan<TypeHandle> GetInstantiation(TypeHandle typeHandle)
|
||||
{
|
||||
if (!typeHandle.IsMethodTable())
|
||||
return default;
|
||||
|
||||
MethodTable methodTable = _methodTables[typeHandle.Address];
|
||||
if (!methodTable.Flags.HasInstantiation)
|
||||
return default;
|
||||
|
||||
return _target.ProcessedData.GetOrAdd<TypeInstantiation>(typeHandle.Address).TypeHandles;
|
||||
}
|
||||
|
||||
private class TypeInstantiation : IData<TypeInstantiation>
|
||||
{
|
||||
public static TypeInstantiation Create(Target target, TargetPointer address) => new TypeInstantiation(target, address);
|
||||
|
||||
public TypeHandle[] TypeHandles { get; }
|
||||
private TypeInstantiation(Target target, TargetPointer typePointer)
|
||||
{
|
||||
RuntimeTypeSystem_1 rts = (RuntimeTypeSystem_1)target.Contracts.RuntimeTypeSystem;
|
||||
MethodTable methodTable = rts._methodTables[typePointer];
|
||||
Debug.Assert(methodTable.Flags.HasInstantiation);
|
||||
|
||||
TargetPointer perInstInfo = methodTable.PerInstInfo;
|
||||
TargetPointer genericsDictInfo = perInstInfo - (ulong)target.PointerSize;
|
||||
|
||||
TargetPointer dictionaryPointer = target.ReadPointer(perInstInfo);
|
||||
|
||||
|
||||
int numberOfGenericArgs = target.ProcessedData.GetOrAdd<GenericsDictInfo>(genericsDictInfo).NumTypeArgs;
|
||||
|
||||
TypeHandles = new TypeHandle[numberOfGenericArgs];
|
||||
for (int i = 0; i < numberOfGenericArgs; i++)
|
||||
{
|
||||
TypeHandles[i] = rts.GetTypeHandle(target.ReadPointer(dictionaryPointer + (ulong)target.PointerSize * (ulong)i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsGenericTypeDefinition(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.IsGenericTypeDefinition;
|
||||
|
||||
public bool HasTypeParam(TypeHandle typeHandle)
|
||||
{
|
||||
if (typeHandle.IsMethodTable())
|
||||
{
|
||||
MethodTable methodTable = _methodTables[typeHandle.Address];
|
||||
return methodTable.Flags.IsArray;
|
||||
}
|
||||
else if (typeHandle.IsTypeDesc())
|
||||
{
|
||||
var typeDesc = _target.ProcessedData.GetOrAdd<TypeDesc>(typeHandle.TypeDescAddress());
|
||||
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
|
||||
switch (elemType)
|
||||
{
|
||||
case CorElementType.ValueType:
|
||||
case CorElementType.Byref:
|
||||
case CorElementType.Ptr:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public CorElementType GetSignatureCorElementType(TypeHandle typeHandle)
|
||||
{
|
||||
if (typeHandle.IsMethodTable())
|
||||
{
|
||||
MethodTable methodTable = _methodTables[typeHandle.Address];
|
||||
|
||||
switch (methodTable.Flags.GetFlag(WFLAGS_HIGH.Category_Mask))
|
||||
{
|
||||
case WFLAGS_HIGH.Category_Array:
|
||||
return CorElementType.Array;
|
||||
case WFLAGS_HIGH.Category_Array | WFLAGS_HIGH.Category_IfArrayThenSzArray:
|
||||
return CorElementType.SzArray;
|
||||
case WFLAGS_HIGH.Category_ValueType:
|
||||
case WFLAGS_HIGH.Category_Nullable:
|
||||
case WFLAGS_HIGH.Category_PrimitiveValueType:
|
||||
return CorElementType.ValueType;
|
||||
case WFLAGS_HIGH.Category_TruePrimitive:
|
||||
return (CorElementType)GetClassData(typeHandle).InternalCorElementType;
|
||||
default:
|
||||
return CorElementType.Class;
|
||||
}
|
||||
}
|
||||
else if (typeHandle.IsTypeDesc())
|
||||
{
|
||||
var typeDesc = _target.ProcessedData.GetOrAdd<TypeDesc>(typeHandle.TypeDescAddress());
|
||||
return (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
// return true if the TypeHandle represents an array, and set the rank to either 0 (if the type is not an array), or the rank number if it is.
|
||||
public bool IsArray(TypeHandle typeHandle, out uint rank)
|
||||
{
|
||||
if (typeHandle.IsMethodTable())
|
||||
{
|
||||
MethodTable methodTable = _methodTables[typeHandle.Address];
|
||||
|
||||
switch (methodTable.Flags.GetFlag(WFLAGS_HIGH.Category_Mask))
|
||||
{
|
||||
case WFLAGS_HIGH.Category_Array:
|
||||
TargetPointer clsPtr = GetClassPointer(typeHandle);
|
||||
rank = _target.ProcessedData.GetOrAdd<Data.ArrayClass>(clsPtr).Rank;
|
||||
return true;
|
||||
|
||||
case WFLAGS_HIGH.Category_Array | WFLAGS_HIGH.Category_IfArrayThenSzArray:
|
||||
rank = 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
rank = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public TypeHandle GetTypeParam(TypeHandle typeHandle)
|
||||
{
|
||||
if (typeHandle.IsMethodTable())
|
||||
{
|
||||
MethodTable methodTable = _methodTables[typeHandle.Address];
|
||||
if (!methodTable.Flags.IsArray)
|
||||
throw new ArgumentException(nameof(typeHandle));
|
||||
|
||||
return GetTypeHandle(methodTable.PerInstInfo);
|
||||
}
|
||||
else if (typeHandle.IsTypeDesc())
|
||||
{
|
||||
var typeDesc = _target.ProcessedData.GetOrAdd<TypeDesc>(typeHandle.TypeDescAddress());
|
||||
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
|
||||
switch (elemType)
|
||||
{
|
||||
case CorElementType.ValueType:
|
||||
case CorElementType.Byref:
|
||||
case CorElementType.Ptr:
|
||||
ParamTypeDesc paramTypeDesc = _target.ProcessedData.GetOrAdd<ParamTypeDesc>(typeHandle.TypeDescAddress());
|
||||
return GetTypeHandle(paramTypeDesc.TypeArg);
|
||||
}
|
||||
}
|
||||
throw new ArgumentException(nameof(typeHandle));
|
||||
}
|
||||
|
||||
public bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token)
|
||||
{
|
||||
module = TargetPointer.Null;
|
||||
token = 0;
|
||||
|
||||
if (!typeHandle.IsTypeDesc())
|
||||
return false;
|
||||
|
||||
var typeDesc = _target.ProcessedData.GetOrAdd<TypeDesc>(typeHandle.TypeDescAddress());
|
||||
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
|
||||
switch (elemType)
|
||||
{
|
||||
case CorElementType.MVar:
|
||||
case CorElementType.Var:
|
||||
TypeVarTypeDesc typeVarTypeDesc = _target.ProcessedData.GetOrAdd<TypeVarTypeDesc>(typeHandle.TypeDescAddress());
|
||||
module = typeVarTypeDesc.Module;
|
||||
token = typeVarTypeDesc.Token;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv)
|
||||
{
|
||||
retAndArgTypes = default;
|
||||
callConv = default;
|
||||
|
||||
if (!typeHandle.IsTypeDesc())
|
||||
return false;
|
||||
|
||||
var typeDesc = _target.ProcessedData.GetOrAdd<TypeDesc>(typeHandle.TypeDescAddress());
|
||||
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
|
||||
if (elemType != CorElementType.FnPtr)
|
||||
return false;
|
||||
|
||||
FnPtrTypeDesc fnPtrTypeDesc = _target.ProcessedData.GetOrAdd<FnPtrTypeDesc>(typeHandle.TypeDescAddress());
|
||||
retAndArgTypes = _target.ProcessedData.GetOrAdd<FunctionPointerRetAndArgs>(typeHandle.TypeDescAddress()).TypeHandles;
|
||||
callConv = (byte)fnPtrTypeDesc.CallConv;
|
||||
return true;
|
||||
}
|
||||
|
||||
private class FunctionPointerRetAndArgs : IData<FunctionPointerRetAndArgs>
|
||||
{
|
||||
public static FunctionPointerRetAndArgs Create(Target target, TargetPointer address) => new FunctionPointerRetAndArgs(target, address);
|
||||
|
||||
public TypeHandle[] TypeHandles { get; }
|
||||
private FunctionPointerRetAndArgs(Target target, TargetPointer typePointer)
|
||||
{
|
||||
RuntimeTypeSystem_1 rts = (RuntimeTypeSystem_1)target.Contracts.RuntimeTypeSystem;
|
||||
FnPtrTypeDesc fnPtrTypeDesc = target.ProcessedData.GetOrAdd<FnPtrTypeDesc>(typePointer);
|
||||
|
||||
TargetPointer retAndArgs = fnPtrTypeDesc.RetAndArgTypes;
|
||||
int numberOfRetAndArgTypes = checked((int)fnPtrTypeDesc.NumArgs + 1);
|
||||
|
||||
TypeHandles = new TypeHandle[numberOfRetAndArgTypes];
|
||||
for (int i = 0; i < numberOfRetAndArgTypes; i++)
|
||||
{
|
||||
TypeHandles[i] = rts.GetTypeHandle(target.ReadPointer(retAndArgs + (ulong)target.PointerSize * (ulong)i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Contracts.RuntimeTypeSystem_1_NS;
|
||||
|
||||
internal static class RuntimeTypeSystem_1_Helpers
|
||||
{
|
||||
public static bool IsTypeDesc(this TypeHandle type)
|
||||
{
|
||||
return type.Address != 0 && ((ulong)type.Address & (ulong)RuntimeTypeSystem_1.TypeHandleBits.ValidMask) == (ulong)RuntimeTypeSystem_1.TypeHandleBits.TypeDesc;
|
||||
}
|
||||
|
||||
public static bool IsMethodTable(this TypeHandle type)
|
||||
{
|
||||
return type.Address != 0 && ((ulong)type.Address & (ulong)RuntimeTypeSystem_1.TypeHandleBits.ValidMask) == (ulong)RuntimeTypeSystem_1.TypeHandleBits.MethodTable;
|
||||
}
|
||||
|
||||
public static TargetPointer TypeDescAddress(this TypeHandle type)
|
||||
{
|
||||
if (!type.IsTypeDesc())
|
||||
return 0;
|
||||
|
||||
return (ulong)type.Address & ~(ulong)RuntimeTypeSystem_1.TypeHandleBits.ValidMask;
|
||||
}
|
||||
}
|
19
src/native/managed/cdacreader/src/Data/DynamicMetadata.cs
Normal file
19
src/native/managed/cdacreader/src/Data/DynamicMetadata.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Data;
|
||||
|
||||
internal class DynamicMetadata : IData<DynamicMetadata>
|
||||
{
|
||||
static DynamicMetadata IData<DynamicMetadata>.Create(Target target, TargetPointer address) => new DynamicMetadata(target, address);
|
||||
public DynamicMetadata(Target target, TargetPointer address)
|
||||
{
|
||||
Target.TypeInfo type = target.GetTypeInfo(DataType.DynamicMetadata);
|
||||
|
||||
Size = target.Read<uint>(address + (ulong)type.Fields[nameof(Size)].Offset);
|
||||
Data = address + (ulong)type.Fields[nameof(Data)].Offset;
|
||||
}
|
||||
|
||||
public uint Size { get; init; }
|
||||
public TargetPointer Data { get; init; }
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Data;
|
||||
|
||||
public sealed class EEClass : IData<EEClass>
|
||||
|
@ -15,9 +13,32 @@ public sealed class EEClass : IData<EEClass>
|
|||
MethodTable = target.ReadPointer(address + (ulong)type.Fields[nameof(MethodTable)].Offset);
|
||||
NumMethods = target.Read<ushort>(address + (ulong)type.Fields[nameof(NumMethods)].Offset);
|
||||
CorTypeAttr = target.Read<uint>(address + (ulong)type.Fields[nameof(CorTypeAttr)].Offset);
|
||||
InternalCorElementType = target.Read<byte>(address + (ulong)type.Fields[nameof(InternalCorElementType)].Offset);
|
||||
}
|
||||
|
||||
public TargetPointer MethodTable { get; init; }
|
||||
public ushort NumMethods { get; init; }
|
||||
public uint CorTypeAttr { get; init; }
|
||||
|
||||
// An InternalCorElementType uses the enum values of a CorElementType to
|
||||
// indicate some of the information about the type of the type which uses
|
||||
// the EEClass
|
||||
//
|
||||
// In particular. All reference types are ELEMENT_TYPE_CLASS
|
||||
// Enums are the element type of their underlying type
|
||||
// ValueTypes which can exactly be represented as an element type are represented as such
|
||||
public byte InternalCorElementType { get; init; }
|
||||
}
|
||||
|
||||
public sealed class ArrayClass : IData<ArrayClass>
|
||||
{
|
||||
static ArrayClass IData<ArrayClass>.Create(Target target, TargetPointer address) => new ArrayClass(target, address);
|
||||
public ArrayClass(Target target, TargetPointer address)
|
||||
{
|
||||
Target.TypeInfo type = target.GetTypeInfo(DataType.ArrayClass);
|
||||
|
||||
Rank = target.Read<byte>(address + (ulong)type.Fields[nameof(Rank)].Offset);
|
||||
}
|
||||
|
||||
public byte Rank { get; init; }
|
||||
}
|
||||
|
|
17
src/native/managed/cdacreader/src/Data/GenericsDictInfo.cs
Normal file
17
src/native/managed/cdacreader/src/Data/GenericsDictInfo.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Data;
|
||||
|
||||
internal class GenericsDictInfo : IData<GenericsDictInfo>
|
||||
{
|
||||
static GenericsDictInfo IData<GenericsDictInfo>.Create(Target target, TargetPointer address) => new GenericsDictInfo(target, address);
|
||||
public GenericsDictInfo(Target target, TargetPointer address)
|
||||
{
|
||||
Target.TypeInfo type = target.GetTypeInfo(DataType.GenericsDictInfo);
|
||||
|
||||
NumTypeArgs = target.Read<ushort>(address + (ulong)type.Fields[nameof(NumTypeArgs)].Offset);
|
||||
}
|
||||
|
||||
public ushort NumTypeArgs { get; init; }
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Data;
|
||||
|
||||
internal sealed class MethodTable : IData<MethodTable>
|
||||
|
@ -20,6 +18,7 @@ internal sealed class MethodTable : IData<MethodTable>
|
|||
ParentMethodTable = target.ReadPointer(address + (ulong)type.Fields[nameof(ParentMethodTable)].Offset);
|
||||
NumInterfaces = target.Read<ushort>(address + (ulong)type.Fields[nameof(NumInterfaces)].Offset);
|
||||
NumVirtuals = target.Read<ushort>(address + (ulong)type.Fields[nameof(NumVirtuals)].Offset);
|
||||
PerInstInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(PerInstInfo)].Offset);
|
||||
}
|
||||
|
||||
public uint MTFlags { get; init; }
|
||||
|
@ -28,6 +27,7 @@ internal sealed class MethodTable : IData<MethodTable>
|
|||
public TargetPointer EEClassOrCanonMT { get; init; }
|
||||
public TargetPointer Module { get; init; }
|
||||
public TargetPointer ParentMethodTable { get; init; }
|
||||
public TargetPointer PerInstInfo { get; init; }
|
||||
public ushort NumInterfaces { get; init; }
|
||||
public ushort NumVirtuals { get; init; }
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Data;
|
||||
|
||||
internal sealed class MethodTableAuxiliaryData : IData<MethodTableAuxiliaryData>
|
||||
|
|
|
@ -22,6 +22,7 @@ internal sealed class Module : IData<Module>
|
|||
Base = target.ReadPointer(address + (ulong)type.Fields[nameof(Base)].Offset);
|
||||
LoaderAllocator = target.ReadPointer(address + (ulong)type.Fields[nameof(LoaderAllocator)].Offset);
|
||||
ThunkHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(ThunkHeap)].Offset);
|
||||
DynamicMetadata = target.ReadPointer(address + (ulong)type.Fields[nameof(DynamicMetadata)].Offset);
|
||||
|
||||
FieldDefToDescMap = target.ReadPointer(address + (ulong)type.Fields[nameof(FieldDefToDescMap)].Offset);
|
||||
ManifestModuleReferencesMap = target.ReadPointer(address + (ulong)type.Fields[nameof(ManifestModuleReferencesMap)].Offset);
|
||||
|
@ -36,6 +37,7 @@ internal sealed class Module : IData<Module>
|
|||
public TargetPointer Base { get; init; }
|
||||
public TargetPointer LoaderAllocator { get; init; }
|
||||
public TargetPointer ThunkHeap { get; init; }
|
||||
public TargetPointer DynamicMetadata { get; init; }
|
||||
|
||||
public TargetPointer FieldDefToDescMap { get; init; }
|
||||
public TargetPointer ManifestModuleReferencesMap { get; init; }
|
||||
|
|
72
src/native/managed/cdacreader/src/Data/TypeDesc.cs
Normal file
72
src/native/managed/cdacreader/src/Data/TypeDesc.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Data;
|
||||
|
||||
internal class TypeDesc : IData<TypeDesc>
|
||||
{
|
||||
static TypeDesc IData<TypeDesc>.Create(Target target, TargetPointer address) => new TypeDesc(target, address);
|
||||
public TypeDesc(Target target, TargetPointer address)
|
||||
{
|
||||
Target.TypeInfo type = target.GetTypeInfo(DataType.TypeDesc);
|
||||
TypeAndFlags = target.Read<uint>(address + (ulong)type.Fields[nameof(TypeAndFlags)].Offset);
|
||||
}
|
||||
|
||||
public uint TypeAndFlags { get; init; }
|
||||
}
|
||||
|
||||
internal class ParamTypeDesc : IData<ParamTypeDesc>
|
||||
{
|
||||
static ParamTypeDesc IData<ParamTypeDesc>.Create(Target target, TargetPointer address) => new ParamTypeDesc(target, address);
|
||||
public ParamTypeDesc(Target target, TargetPointer address)
|
||||
{
|
||||
Target.TypeInfo type = target.GetTypeInfo(DataType.TypeDesc);
|
||||
TypeAndFlags = target.Read<uint>(address + (ulong)type.Fields[nameof(TypeAndFlags)].Offset);
|
||||
|
||||
type = target.GetTypeInfo(DataType.ParamTypeDesc);
|
||||
TypeArg = target.Read<ushort>(address + (ulong)type.Fields[nameof(TypeArg)].Offset);
|
||||
}
|
||||
|
||||
public uint TypeAndFlags { get; init; }
|
||||
public TargetPointer TypeArg { get; init; }
|
||||
}
|
||||
|
||||
internal class TypeVarTypeDesc : IData<TypeVarTypeDesc>
|
||||
{
|
||||
static TypeVarTypeDesc IData<TypeVarTypeDesc>.Create(Target target, TargetPointer address) => new TypeVarTypeDesc(target, address);
|
||||
public TypeVarTypeDesc(Target target, TargetPointer address)
|
||||
{
|
||||
Target.TypeInfo type = target.GetTypeInfo(DataType.TypeDesc);
|
||||
TypeAndFlags = target.Read<uint>(address + (ulong)type.Fields[nameof(TypeAndFlags)].Offset);
|
||||
|
||||
type = target.GetTypeInfo(DataType.TypeVarTypeDesc);
|
||||
|
||||
Module = target.ReadPointer(address + (ulong)type.Fields[nameof(Module)].Offset);
|
||||
Token = target.Read<uint>(address + (ulong)type.Fields[nameof(Token)].Offset);
|
||||
}
|
||||
|
||||
public uint TypeAndFlags { get; init; }
|
||||
public TargetPointer Module { get; init; }
|
||||
public uint Token { get; init; }
|
||||
}
|
||||
|
||||
internal class FnPtrTypeDesc : IData<FnPtrTypeDesc>
|
||||
{
|
||||
static FnPtrTypeDesc IData<FnPtrTypeDesc>.Create(Target target, TargetPointer address) => new FnPtrTypeDesc(target, address);
|
||||
public FnPtrTypeDesc(Target target, TargetPointer address)
|
||||
{
|
||||
Target.TypeInfo type = target.GetTypeInfo(DataType.TypeDesc);
|
||||
TypeAndFlags = target.Read<uint>(address + (ulong)type.Fields[nameof(TypeAndFlags)].Offset);
|
||||
|
||||
type = target.GetTypeInfo(DataType.FnPtrTypeDesc);
|
||||
|
||||
NumArgs = target.Read<uint>(address + (ulong)type.Fields[nameof(NumArgs)].Offset);
|
||||
CallConv = target.Read<uint>(address + (ulong)type.Fields[nameof(CallConv)].Offset);
|
||||
RetAndArgTypes = (TargetPointer)(address + (ulong)type.Fields[nameof(RetAndArgTypes)].Offset);
|
||||
}
|
||||
|
||||
public uint TypeAndFlags { get; init; }
|
||||
public uint NumArgs { get; init; }
|
||||
public uint CallConv { get; init; }
|
||||
public TargetPointer RetAndArgTypes { get; init; }
|
||||
}
|
|
@ -29,5 +29,12 @@ public enum DataType
|
|||
Module,
|
||||
MethodTable,
|
||||
EEClass,
|
||||
ArrayClass,
|
||||
MethodTableAuxiliaryData,
|
||||
GenericsDictInfo,
|
||||
TypeDesc,
|
||||
ParamTypeDesc,
|
||||
TypeVarTypeDesc,
|
||||
FnPtrTypeDesc,
|
||||
DynamicMetadata,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
// 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.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Diagnostics.DataContractReader.Contracts;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Helpers;
|
||||
|
||||
internal partial class EcmaMetadataReader
|
||||
{
|
||||
private int ColumnSize(MetadataColumnIndex column)
|
||||
{
|
||||
return columnSize[(int)column];
|
||||
}
|
||||
|
||||
private int ColumnOffset(MetadataColumnIndex column)
|
||||
{
|
||||
return columnOffset[(int)column];
|
||||
}
|
||||
|
||||
private int RowCount(MetadataTable table)
|
||||
{
|
||||
return _ecmaMetadata.Schema.RowCount[(int)table];
|
||||
}
|
||||
|
||||
private bool TryReadTableEntry(ReadOnlySpan<byte> bytes, MetadataColumnIndex column, out uint value)
|
||||
{
|
||||
int size = ColumnSize(column);
|
||||
ReadOnlySpan<byte> singleColumn = bytes.Slice(ColumnOffset(column), size);
|
||||
if (size == 2)
|
||||
{
|
||||
if (TryReadCore<ushort>(singleColumn, out ushort valueAsShort))
|
||||
{
|
||||
value = valueAsShort;
|
||||
return true;
|
||||
}
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
if (size != 4)
|
||||
throw new ArgumentOutOfRangeException(nameof(column));
|
||||
|
||||
return TryReadCore(singleColumn, out value);
|
||||
}
|
||||
|
||||
private uint GetColumnRaw(EcmaMetadataCursor c, MetadataColumnIndex col_idx)
|
||||
{
|
||||
if (c.Table != ColumnTable(col_idx))
|
||||
throw new ArgumentOutOfRangeException(nameof(col_idx));
|
||||
|
||||
if (!TryReadTableEntry(c.Row, col_idx, out uint rawResult))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(col_idx));
|
||||
}
|
||||
return rawResult;
|
||||
}
|
||||
|
||||
private int RidEncodingBits(MetadataTable table)
|
||||
{
|
||||
if (table == MetadataTable.Unused)
|
||||
return 0;
|
||||
|
||||
int countInTable = RowCount(table);
|
||||
|
||||
// Tables start at 1
|
||||
countInTable++;
|
||||
return 32 - BitOperations.LeadingZeroCount((uint)countInTable);
|
||||
}
|
||||
|
||||
private int RidEncodingBytes(MetadataTable table)
|
||||
{
|
||||
if (RidEncodingBits(table) > 16)
|
||||
return 4;
|
||||
else
|
||||
return 2;
|
||||
}
|
||||
|
||||
private int CodedIndexEncodingBytes(ReadOnlySpan<MetadataTable> tablesEncoded)
|
||||
{
|
||||
uint encodingMask = BitOperations.RoundUpToPowerOf2((uint)tablesEncoded.Length) - 1;
|
||||
int bitsForTableEncoding = 32 - BitOperations.LeadingZeroCount(encodingMask);
|
||||
if (tablesEncoded.Length == 1)
|
||||
{
|
||||
Debug.Assert(bitsForTableEncoding == 0); // This is just a rid to token conversion, no extra bits.
|
||||
}
|
||||
if (tablesEncoded.Length == 3 && tablesEncoded[0] == (MetadataTable)(-2))
|
||||
{
|
||||
// Ptr scenario
|
||||
return RidEncodingBytes(tablesEncoded[2]);
|
||||
}
|
||||
|
||||
foreach (MetadataTable table in tablesEncoded)
|
||||
{
|
||||
if ((RidEncodingBits(table) + bitsForTableEncoding) > 16)
|
||||
return 4;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,594 @@
|
|||
// 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.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Diagnostics.DataContractReader.Contracts;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Helpers;
|
||||
|
||||
internal partial class EcmaMetadataReader
|
||||
{
|
||||
private enum ColumnType
|
||||
{
|
||||
Unknown,
|
||||
TwoByteConstant,
|
||||
FourByteConstant,
|
||||
Utf8String,
|
||||
Blob,
|
||||
Guid,
|
||||
Token
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum PtrTablesPresent
|
||||
{
|
||||
None = 0,
|
||||
Method = 1,
|
||||
Field = 2,
|
||||
Param = 4,
|
||||
Property = 8,
|
||||
Event = 16
|
||||
}
|
||||
|
||||
private static readonly MetadataTable[] columnTable = GetColumnTables();
|
||||
private static readonly ColumnType[] columnTypes = GetColumnTypes();
|
||||
private static readonly Func<uint, uint>[][] columnTokenDecoders = GetColumnTokenDecoders();
|
||||
private static readonly MetadataTable[][] codedIndexDecoderRing = ColumnDecodeData.GetCodedIndexDecoderRing();
|
||||
|
||||
private static ColumnType[] GetColumnTypes()
|
||||
{
|
||||
ColumnType[] columnTypes = new ColumnType[(int)MetadataColumnIndex.Count];
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.Module_Generation] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.Module_Name] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.Module_Mvid] = ColumnType.Guid;
|
||||
columnTypes[(int)MetadataColumnIndex.Module_EncId] = ColumnType.Guid;
|
||||
columnTypes[(int)MetadataColumnIndex.Module_EncBaseId] = ColumnType.Guid;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.TypeRef_ResolutionScope] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.TypeRef_TypeName] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.TypeRef_TypeNamespace] = ColumnType.Utf8String;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.TypeDef_Flags] = ColumnType.FourByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.TypeDef_TypeName] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.TypeDef_TypeNamespace] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.TypeDef_Extends] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.TypeDef_FieldList] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.TypeDef_MethodList] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.FieldPtr_Field] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.Field_Flags] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.Field_Name] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.Field_Signature] = ColumnType.Blob;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.MethodPtr_Method] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.MethodDef_Rva] = ColumnType.FourByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.MethodDef_ImplFlags] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.MethodDef_Flags] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.MethodDef_Name] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.MethodDef_Signature] = ColumnType.Blob;
|
||||
columnTypes[(int)MetadataColumnIndex.MethodDef_ParamList] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.ParamPtr_Param] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.Param_Flags] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.Param_Sequence] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.Param_Name] = ColumnType.Utf8String;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.InterfaceImpl_Class] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.InterfaceImpl_Interface] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.MemberRef_Class] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.MemberRef_Name] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.MemberRef_Signature] = ColumnType.Blob;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.Constant_Type] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.Constant_Parent] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.Constant_Value] = ColumnType.Blob;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.CustomAttribute_Parent] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.CustomAttribute_Type] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.CustomAttribute_Value] = ColumnType.Blob;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.FieldMarshal_Parent] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.FieldMarshal_NativeType] = ColumnType.Blob;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.DeclSecurity_Action] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.DeclSecurity_Parent] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.DeclSecurity_PermissionSet] = ColumnType.Blob;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.ClassLayout_PackingSize] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.ClassLayout_ClassSize] = ColumnType.FourByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.ClassLayout_Parent] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.FieldLayout_Offset] = ColumnType.FourByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.FieldLayout_Field] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.StandAloneSig_Signature] = ColumnType.Blob;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.EventMap_Parent] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.EventMap_EventList] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.EventPtr_Event] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.Event_EventFlags] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.Event_Name] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.Event_EventType] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.PropertyMap_Parent] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.PropertyMap_PropertyList] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.PropertyPtr_Property] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.Property_Flags] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.Property_Name] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.Property_Type] = ColumnType.Blob;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.MethodSemantics_Semantics] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.MethodSemantics_Method] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.MethodSemantics_Association] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.MethodImpl_Class] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.MethodImpl_MethodBody] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.MethodImpl_MethodDeclaration] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.ModuleRef_Name] = ColumnType.Utf8String;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.TypeSpec_Signature] = ColumnType.Blob;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.ImplMap_MappingFlags] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.ImplMap_MemberForwarded] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.ImplMap_ImportName] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.ImplMap_ImportScope] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.FieldRva_Rva] = ColumnType.FourByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.FieldRva_Field] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.ENCLog_Token] = ColumnType.FourByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.ENCLog_Op] = ColumnType.FourByteConstant;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.ENCMap_Token] = ColumnType.FourByteConstant;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.Assembly_HashAlgId] = ColumnType.FourByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.Assembly_MajorVersion] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.Assembly_MinorVersion] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.Assembly_BuildNumber] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.Assembly_RevisionNumber] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.Assembly_Flags] = ColumnType.FourByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.Assembly_PublicKey] = ColumnType.Blob;
|
||||
columnTypes[(int)MetadataColumnIndex.Assembly_Name] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.Assembly_Culture] = ColumnType.Utf8String;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.AssemblyRef_MajorVersion] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.AssemblyRef_MinorVersion] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.AssemblyRef_BuildNumber] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.AssemblyRef_RevisionNumber] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.AssemblyRef_Flags] = ColumnType.FourByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.AssemblyRef_PublicKeyOrToken] = ColumnType.Blob;
|
||||
columnTypes[(int)MetadataColumnIndex.AssemblyRef_Name] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.AssemblyRef_Culture] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.AssemblyRef_HashValue] = ColumnType.Blob;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.File_Flags] = ColumnType.FourByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.File_Name] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.File_HashValue] = ColumnType.Blob;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.ExportedType_Flags] = ColumnType.FourByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.ExportedType_TypeDefId] = ColumnType.FourByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.ExportedType_TypeName] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.ExportedType_TypeNamespace] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.ExportedType_Implementation] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.ManifestResource_Offset] = ColumnType.FourByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.ManifestResource_Flags] = ColumnType.FourByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.ManifestResource_Name] = ColumnType.Utf8String;
|
||||
columnTypes[(int)MetadataColumnIndex.ManifestResource_Implementation] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.NestedClass_NestedClass] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.NestedClass_EnclosingClass] = ColumnType.Token;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.GenericParam_Number] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.GenericParam_Flags] = ColumnType.TwoByteConstant;
|
||||
columnTypes[(int)MetadataColumnIndex.GenericParam_Owner] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.GenericParam_Name] = ColumnType.Utf8String;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.MethodSpec_Method] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.MethodSpec_Instantiation] = ColumnType.Blob;
|
||||
|
||||
columnTypes[(int)MetadataColumnIndex.GenericParamConstraint_Owner] = ColumnType.Token;
|
||||
columnTypes[(int)MetadataColumnIndex.GenericParamConstraint_Constraint] = ColumnType.Token;
|
||||
|
||||
return columnTypes;
|
||||
}
|
||||
|
||||
private static MetadataTable[] GetColumnTables()
|
||||
{
|
||||
MetadataTable[] metadataTables = new MetadataTable[(int)MetadataColumnIndex.Count];
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.Module_Generation] = MetadataTable.Module;
|
||||
metadataTables[(int)MetadataColumnIndex.Module_Name] = MetadataTable.Module;
|
||||
metadataTables[(int)MetadataColumnIndex.Module_Mvid] = MetadataTable.Module;
|
||||
metadataTables[(int)MetadataColumnIndex.Module_EncId] = MetadataTable.Module;
|
||||
metadataTables[(int)MetadataColumnIndex.Module_EncBaseId] = MetadataTable.Module;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.TypeRef_ResolutionScope] = MetadataTable.TypeRef;
|
||||
metadataTables[(int)MetadataColumnIndex.TypeRef_TypeName] = MetadataTable.TypeRef;
|
||||
metadataTables[(int)MetadataColumnIndex.TypeRef_TypeNamespace] = MetadataTable.TypeRef;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.TypeDef_Flags] = MetadataTable.TypeDef;
|
||||
metadataTables[(int)MetadataColumnIndex.TypeDef_TypeName] = MetadataTable.TypeDef;
|
||||
metadataTables[(int)MetadataColumnIndex.TypeDef_TypeNamespace] = MetadataTable.TypeDef;
|
||||
metadataTables[(int)MetadataColumnIndex.TypeDef_Extends] = MetadataTable.TypeDef;
|
||||
metadataTables[(int)MetadataColumnIndex.TypeDef_FieldList] = MetadataTable.TypeDef;
|
||||
metadataTables[(int)MetadataColumnIndex.TypeDef_MethodList] = MetadataTable.TypeDef;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.FieldPtr_Field] = MetadataTable.FieldPtr;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.Field_Flags] = MetadataTable.Field;
|
||||
metadataTables[(int)MetadataColumnIndex.Field_Name] = MetadataTable.Field;
|
||||
metadataTables[(int)MetadataColumnIndex.Field_Signature] = MetadataTable.Field;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.MethodPtr_Method] = MetadataTable.MethodPtr;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.MethodDef_Rva] = MetadataTable.MethodDef;
|
||||
metadataTables[(int)MetadataColumnIndex.MethodDef_ImplFlags] = MetadataTable.MethodDef;
|
||||
metadataTables[(int)MetadataColumnIndex.MethodDef_Flags] = MetadataTable.MethodDef;
|
||||
metadataTables[(int)MetadataColumnIndex.MethodDef_Name] = MetadataTable.MethodDef;
|
||||
metadataTables[(int)MetadataColumnIndex.MethodDef_Signature] = MetadataTable.MethodDef;
|
||||
metadataTables[(int)MetadataColumnIndex.MethodDef_ParamList] = MetadataTable.MethodDef;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.ParamPtr_Param] = MetadataTable.ParamPtr;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.Param_Flags] = MetadataTable.Param;
|
||||
metadataTables[(int)MetadataColumnIndex.Param_Sequence] = MetadataTable.Param;
|
||||
metadataTables[(int)MetadataColumnIndex.Param_Name] = MetadataTable.Param;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.InterfaceImpl_Class] = MetadataTable.InterfaceImpl;
|
||||
metadataTables[(int)MetadataColumnIndex.InterfaceImpl_Interface] = MetadataTable.InterfaceImpl;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.MemberRef_Class] = MetadataTable.MemberRef;
|
||||
metadataTables[(int)MetadataColumnIndex.MemberRef_Name] = MetadataTable.MemberRef;
|
||||
metadataTables[(int)MetadataColumnIndex.MemberRef_Signature] = MetadataTable.MemberRef;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.Constant_Type] = MetadataTable.Constant;
|
||||
metadataTables[(int)MetadataColumnIndex.Constant_Parent] = MetadataTable.Constant;
|
||||
metadataTables[(int)MetadataColumnIndex.Constant_Value] = MetadataTable.Constant;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.CustomAttribute_Parent] = MetadataTable.CustomAttribute;
|
||||
metadataTables[(int)MetadataColumnIndex.CustomAttribute_Type] = MetadataTable.CustomAttribute;
|
||||
metadataTables[(int)MetadataColumnIndex.CustomAttribute_Value] = MetadataTable.CustomAttribute;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.FieldMarshal_Parent] = MetadataTable.FieldMarshal;
|
||||
metadataTables[(int)MetadataColumnIndex.FieldMarshal_NativeType] = MetadataTable.FieldMarshal;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.DeclSecurity_Action] = MetadataTable.DeclSecurity;
|
||||
metadataTables[(int)MetadataColumnIndex.DeclSecurity_Parent] = MetadataTable.DeclSecurity;
|
||||
metadataTables[(int)MetadataColumnIndex.DeclSecurity_PermissionSet] = MetadataTable.DeclSecurity;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.ClassLayout_PackingSize] = MetadataTable.ClassLayout;
|
||||
metadataTables[(int)MetadataColumnIndex.ClassLayout_ClassSize] = MetadataTable.ClassLayout;
|
||||
metadataTables[(int)MetadataColumnIndex.ClassLayout_Parent] = MetadataTable.ClassLayout;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.FieldLayout_Offset] = MetadataTable.FieldLayout;
|
||||
metadataTables[(int)MetadataColumnIndex.FieldLayout_Field] = MetadataTable.FieldLayout;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.StandAloneSig_Signature] = MetadataTable.StandAloneSig;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.EventMap_Parent] = MetadataTable.EventMap;
|
||||
metadataTables[(int)MetadataColumnIndex.EventMap_EventList] = MetadataTable.EventMap;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.EventPtr_Event] = MetadataTable.EventPtr;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.Event_EventFlags] = MetadataTable.Event;
|
||||
metadataTables[(int)MetadataColumnIndex.Event_Name] = MetadataTable.Event;
|
||||
metadataTables[(int)MetadataColumnIndex.Event_EventType] = MetadataTable.Event;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.PropertyMap_Parent] = MetadataTable.PropertyMap;
|
||||
metadataTables[(int)MetadataColumnIndex.PropertyMap_PropertyList] = MetadataTable.PropertyMap;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.PropertyPtr_Property] = MetadataTable.PropertyPtr;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.Property_Flags] = MetadataTable.Property;
|
||||
metadataTables[(int)MetadataColumnIndex.Property_Name] = MetadataTable.Property;
|
||||
metadataTables[(int)MetadataColumnIndex.Property_Type] = MetadataTable.Property;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.MethodSemantics_Semantics] = MetadataTable.MethodSemantics;
|
||||
metadataTables[(int)MetadataColumnIndex.MethodSemantics_Method] = MetadataTable.MethodSemantics;
|
||||
metadataTables[(int)MetadataColumnIndex.MethodSemantics_Association] = MetadataTable.MethodSemantics;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.MethodImpl_Class] = MetadataTable.MethodImpl;
|
||||
metadataTables[(int)MetadataColumnIndex.MethodImpl_MethodBody] = MetadataTable.MethodImpl;
|
||||
metadataTables[(int)MetadataColumnIndex.MethodImpl_MethodDeclaration] = MetadataTable.MethodImpl;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.ModuleRef_Name] = MetadataTable.ModuleRef;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.TypeSpec_Signature] = MetadataTable.TypeSpec;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.ImplMap_MappingFlags] = MetadataTable.ImplMap;
|
||||
metadataTables[(int)MetadataColumnIndex.ImplMap_MemberForwarded] = MetadataTable.ImplMap;
|
||||
metadataTables[(int)MetadataColumnIndex.ImplMap_ImportName] = MetadataTable.ImplMap;
|
||||
metadataTables[(int)MetadataColumnIndex.ImplMap_ImportScope] = MetadataTable.ImplMap;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.FieldRva_Rva] = MetadataTable.FieldRva;
|
||||
metadataTables[(int)MetadataColumnIndex.FieldRva_Field] = MetadataTable.FieldRva;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.ENCLog_Token] = MetadataTable.ENCLog;
|
||||
metadataTables[(int)MetadataColumnIndex.ENCLog_Op] = MetadataTable.ENCLog;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.ENCMap_Token] = MetadataTable.ENCMap;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.Assembly_HashAlgId] = MetadataTable.Assembly;
|
||||
metadataTables[(int)MetadataColumnIndex.Assembly_MajorVersion] = MetadataTable.Assembly;
|
||||
metadataTables[(int)MetadataColumnIndex.Assembly_MinorVersion] = MetadataTable.Assembly;
|
||||
metadataTables[(int)MetadataColumnIndex.Assembly_BuildNumber] = MetadataTable.Assembly;
|
||||
metadataTables[(int)MetadataColumnIndex.Assembly_RevisionNumber] = MetadataTable.Assembly;
|
||||
metadataTables[(int)MetadataColumnIndex.Assembly_Flags] = MetadataTable.Assembly;
|
||||
metadataTables[(int)MetadataColumnIndex.Assembly_PublicKey] = MetadataTable.Assembly;
|
||||
metadataTables[(int)MetadataColumnIndex.Assembly_Name] = MetadataTable.Assembly;
|
||||
metadataTables[(int)MetadataColumnIndex.Assembly_Culture] = MetadataTable.Assembly;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.AssemblyRef_MajorVersion] = MetadataTable.AssemblyRef;
|
||||
metadataTables[(int)MetadataColumnIndex.AssemblyRef_MinorVersion] = MetadataTable.AssemblyRef;
|
||||
metadataTables[(int)MetadataColumnIndex.AssemblyRef_BuildNumber] = MetadataTable.AssemblyRef;
|
||||
metadataTables[(int)MetadataColumnIndex.AssemblyRef_RevisionNumber] = MetadataTable.AssemblyRef;
|
||||
metadataTables[(int)MetadataColumnIndex.AssemblyRef_Flags] = MetadataTable.AssemblyRef;
|
||||
metadataTables[(int)MetadataColumnIndex.AssemblyRef_PublicKeyOrToken] = MetadataTable.AssemblyRef;
|
||||
metadataTables[(int)MetadataColumnIndex.AssemblyRef_Name] = MetadataTable.AssemblyRef;
|
||||
metadataTables[(int)MetadataColumnIndex.AssemblyRef_Culture] = MetadataTable.AssemblyRef;
|
||||
metadataTables[(int)MetadataColumnIndex.AssemblyRef_HashValue] = MetadataTable.AssemblyRef;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.File_Flags] = MetadataTable.File;
|
||||
metadataTables[(int)MetadataColumnIndex.File_Name] = MetadataTable.File;
|
||||
metadataTables[(int)MetadataColumnIndex.File_HashValue] = MetadataTable.File;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.ExportedType_Flags] = MetadataTable.ExportedType;
|
||||
metadataTables[(int)MetadataColumnIndex.ExportedType_TypeDefId] = MetadataTable.ExportedType;
|
||||
metadataTables[(int)MetadataColumnIndex.ExportedType_TypeName] = MetadataTable.ExportedType;
|
||||
metadataTables[(int)MetadataColumnIndex.ExportedType_TypeNamespace] = MetadataTable.ExportedType;
|
||||
metadataTables[(int)MetadataColumnIndex.ExportedType_Implementation] = MetadataTable.ExportedType;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.ManifestResource_Offset] = MetadataTable.ManifestResource;
|
||||
metadataTables[(int)MetadataColumnIndex.ManifestResource_Flags] = MetadataTable.ManifestResource;
|
||||
metadataTables[(int)MetadataColumnIndex.ManifestResource_Name] = MetadataTable.ManifestResource;
|
||||
metadataTables[(int)MetadataColumnIndex.ManifestResource_Implementation] = MetadataTable.ManifestResource;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.NestedClass_NestedClass] = MetadataTable.NestedClass;
|
||||
metadataTables[(int)MetadataColumnIndex.NestedClass_EnclosingClass] = MetadataTable.NestedClass;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.GenericParam_Number] = MetadataTable.GenericParam;
|
||||
metadataTables[(int)MetadataColumnIndex.GenericParam_Flags] = MetadataTable.GenericParam;
|
||||
metadataTables[(int)MetadataColumnIndex.GenericParam_Owner] = MetadataTable.GenericParam;
|
||||
metadataTables[(int)MetadataColumnIndex.GenericParam_Name] = MetadataTable.GenericParam;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.MethodSpec_Method] = MetadataTable.MethodSpec;
|
||||
metadataTables[(int)MetadataColumnIndex.MethodSpec_Instantiation] = MetadataTable.MethodSpec;
|
||||
|
||||
metadataTables[(int)MetadataColumnIndex.GenericParamConstraint_Owner] = MetadataTable.GenericParamConstraint;
|
||||
metadataTables[(int)MetadataColumnIndex.GenericParamConstraint_Constraint] = MetadataTable.GenericParamConstraint;
|
||||
|
||||
return metadataTables;
|
||||
}
|
||||
|
||||
private static Func<uint, uint>[][] GetColumnTokenDecoders()
|
||||
{
|
||||
Func<uint, uint>[][] decoders = new Func<uint, uint>[32][];
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
List<MetadataTable> ptrTablesPresent = new();
|
||||
PtrTablesPresent tablesPresent = (PtrTablesPresent)i;
|
||||
if (tablesPresent.HasFlag(PtrTablesPresent.Field))
|
||||
{
|
||||
ptrTablesPresent.Add(MetadataTable.FieldPtr);
|
||||
}
|
||||
if (tablesPresent.HasFlag(PtrTablesPresent.Param))
|
||||
{
|
||||
ptrTablesPresent.Add(MetadataTable.ParamPtr);
|
||||
}
|
||||
if (tablesPresent.HasFlag(PtrTablesPresent.Param))
|
||||
{
|
||||
ptrTablesPresent.Add(MetadataTable.ParamPtr);
|
||||
}
|
||||
if (tablesPresent.HasFlag(PtrTablesPresent.Property))
|
||||
{
|
||||
ptrTablesPresent.Add(MetadataTable.PropertyPtr);
|
||||
}
|
||||
if (tablesPresent.HasFlag(PtrTablesPresent.Event))
|
||||
{
|
||||
ptrTablesPresent.Add(MetadataTable.EventPtr);
|
||||
}
|
||||
|
||||
decoders[i] = GetColumnTokenDecode(ptrTablesPresent);
|
||||
}
|
||||
return decoders;
|
||||
}
|
||||
|
||||
private static class ColumnDecodeData
|
||||
{
|
||||
private static readonly MetadataTable[] TypeDefOrRef = { MetadataTable.TypeDef, MetadataTable.TypeRef, MetadataTable.TypeSpec };
|
||||
private static readonly MetadataTable[] HasConstant = { MetadataTable.Field, MetadataTable.Param, MetadataTable.Property };
|
||||
private static readonly MetadataTable[] HasCustomAttribute =
|
||||
{
|
||||
MetadataTable.MethodDef,
|
||||
MetadataTable.Field,
|
||||
MetadataTable.TypeRef,
|
||||
MetadataTable.TypeDef,
|
||||
MetadataTable.Param,
|
||||
MetadataTable.InterfaceImpl,
|
||||
MetadataTable.MemberRef,
|
||||
MetadataTable.Module,
|
||||
MetadataTable.DeclSecurity,
|
||||
MetadataTable.Property,
|
||||
MetadataTable.Event,
|
||||
MetadataTable.StandAloneSig,
|
||||
MetadataTable.ModuleRef,
|
||||
MetadataTable.TypeSpec,
|
||||
MetadataTable.Assembly,
|
||||
MetadataTable.AssemblyRef,
|
||||
MetadataTable.File,
|
||||
MetadataTable.ExportedType,
|
||||
MetadataTable.ManifestResource,
|
||||
MetadataTable.GenericParam,
|
||||
MetadataTable.GenericParamConstraint,
|
||||
MetadataTable.MethodSpec };
|
||||
|
||||
private static readonly MetadataTable[] HasFieldMarshal = { MetadataTable.Field, MetadataTable.Param };
|
||||
private static readonly MetadataTable[] HasDeclSecurity = { MetadataTable.TypeDef, MetadataTable.MethodDef, MetadataTable.Assembly };
|
||||
private static readonly MetadataTable[] MemberRefParent = { MetadataTable.TypeDef, MetadataTable.TypeRef, MetadataTable.ModuleRef, MetadataTable.MethodDef, MetadataTable.TypeSpec };
|
||||
private static readonly MetadataTable[] HasSemantics = { MetadataTable.Event, MetadataTable.Property };
|
||||
private static readonly MetadataTable[] MethodDefOrRef = { MetadataTable.MethodDef, MetadataTable.MemberRef };
|
||||
private static readonly MetadataTable[] MemberForwarded = { MetadataTable.Field, MetadataTable.MethodDef };
|
||||
private static readonly MetadataTable[] Implementation = { MetadataTable.File, MetadataTable.AssemblyRef, MetadataTable.ExportedType };
|
||||
private static readonly MetadataTable[] CustomAttributeType = { MetadataTable.Unused, MetadataTable.Unused, MetadataTable.MethodDef, MetadataTable.MemberRef, MetadataTable.Unused };
|
||||
private static readonly MetadataTable[] ResolutionScope = { MetadataTable.Module, MetadataTable.ModuleRef, MetadataTable.AssemblyRef, MetadataTable.TypeRef };
|
||||
private static readonly MetadataTable[] TypeOrMethodDef = { MetadataTable.TypeDef, MetadataTable.MethodDef };
|
||||
|
||||
private static readonly MetadataTable[] FieldOrFieldPtr = { (MetadataTable)(-2), MetadataTable.Field, MetadataTable.FieldPtr };
|
||||
private static readonly MetadataTable[] MethodDefOrMethodPtr = { (MetadataTable)(-2), MetadataTable.MethodDef, MetadataTable.MethodPtr };
|
||||
private static readonly MetadataTable[] ParamOrParamPtr = { (MetadataTable)(-2), MetadataTable.Param, MetadataTable.ParamPtr };
|
||||
private static readonly MetadataTable[] EventOrEventPtr = { (MetadataTable)(-2), MetadataTable.Event, MetadataTable.EventPtr };
|
||||
private static readonly MetadataTable[] PropertyOrPropertyPtr = { (MetadataTable)(-2), MetadataTable.Property, MetadataTable.PropertyPtr };
|
||||
|
||||
public static MetadataTable[][] GetCodedIndexDecoderRing()
|
||||
{
|
||||
MetadataTable[][] decoderRing = new MetadataTable[(int)MetadataColumnIndex.Count][];
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.TypeRef_ResolutionScope] = ResolutionScope;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.TypeDef_Extends] = TypeDefOrRef;
|
||||
decoderRing[(int)MetadataColumnIndex.TypeDef_FieldList] = FieldOrFieldPtr;
|
||||
decoderRing[(int)MetadataColumnIndex.TypeDef_MethodList] = MethodDefOrMethodPtr;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.FieldPtr_Field] = new[] { MetadataTable.Field };
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.MethodPtr_Method] = new[] { MetadataTable.MethodDef };
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.MethodDef_ParamList] = ParamOrParamPtr;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.ParamPtr_Param] = new[] { MetadataTable.Param };
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.InterfaceImpl_Class] = new[] { MetadataTable.TypeDef };
|
||||
decoderRing[(int)MetadataColumnIndex.InterfaceImpl_Interface] = TypeDefOrRef;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.MemberRef_Class] = MemberRefParent;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.Constant_Parent] = HasConstant;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.CustomAttribute_Parent] = HasCustomAttribute;
|
||||
decoderRing[(int)MetadataColumnIndex.CustomAttribute_Type] = CustomAttributeType;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.FieldMarshal_Parent] = HasFieldMarshal;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.DeclSecurity_Parent] = HasDeclSecurity;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.ClassLayout_Parent] = new[] { MetadataTable.TypeDef };
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.FieldLayout_Field] = new[] { MetadataTable.Field };
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.EventMap_Parent] = new[] { MetadataTable.TypeDef };
|
||||
decoderRing[(int)MetadataColumnIndex.EventMap_EventList] = EventOrEventPtr;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.EventPtr_Event] = new[] { MetadataTable.Event };
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.Event_EventType] = TypeDefOrRef;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.PropertyMap_Parent] = new[] { MetadataTable.TypeDef };
|
||||
decoderRing[(int)MetadataColumnIndex.PropertyMap_PropertyList] = PropertyOrPropertyPtr;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.PropertyPtr_Property] = new[] { MetadataTable.Property };
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.MethodSemantics_Method] = new[] { MetadataTable.MethodDef };
|
||||
decoderRing[(int)MetadataColumnIndex.MethodSemantics_Association] = HasSemantics;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.MethodImpl_Class] = new[] { MetadataTable.TypeDef };
|
||||
decoderRing[(int)MetadataColumnIndex.MethodImpl_MethodBody] = MethodDefOrRef;
|
||||
decoderRing[(int)MetadataColumnIndex.MethodImpl_MethodDeclaration] = MethodDefOrRef;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.ImplMap_MemberForwarded] = MemberForwarded;
|
||||
decoderRing[(int)MetadataColumnIndex.ImplMap_ImportScope] = new[] { MetadataTable.ModuleRef };
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.FieldRva_Field] = new[] { MetadataTable.ModuleRef };
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.ExportedType_Implementation] = Implementation;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.ManifestResource_Implementation] = Implementation;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.NestedClass_NestedClass] = new[] { MetadataTable.TypeDef };
|
||||
decoderRing[(int)MetadataColumnIndex.NestedClass_EnclosingClass] = new[] { MetadataTable.TypeDef };
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.GenericParam_Owner] = TypeOrMethodDef;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.MethodSpec_Method] = MethodDefOrRef;
|
||||
|
||||
decoderRing[(int)MetadataColumnIndex.GenericParamConstraint_Owner] = new[] { MetadataTable.GenericParam };
|
||||
decoderRing[(int)MetadataColumnIndex.GenericParamConstraint_Constraint] = TypeDefOrRef;
|
||||
|
||||
return decoderRing;
|
||||
}
|
||||
}
|
||||
|
||||
private static uint DecodeCodedIndex(uint input, ReadOnlySpan<MetadataTable> tablesEncoded)
|
||||
{
|
||||
uint encodingMask = BitOperations.RoundUpToPowerOf2((uint)tablesEncoded.Length) - 1;
|
||||
int bitsForTableEncoding = 32 - BitOperations.LeadingZeroCount(BitOperations.RoundUpToPowerOf2((uint)tablesEncoded.Length) - 1);
|
||||
MetadataTable table = tablesEncoded[(int)(input & encodingMask)];
|
||||
uint rid = input >> bitsForTableEncoding;
|
||||
return CreateToken(table, rid);
|
||||
}
|
||||
|
||||
private static Func<uint, uint>[] GetColumnTokenDecode(List<MetadataTable> ptrTablesPresent)
|
||||
{
|
||||
Func<uint, uint>[] columnTokenDecode = new Func<uint, uint>[(int)MetadataColumnIndex.Count];
|
||||
MetadataTable[][] decoderRing = ColumnDecodeData.GetCodedIndexDecoderRing();
|
||||
for (int i = 0; i < decoderRing.Length; i++)
|
||||
{
|
||||
if (decoderRing[i] != null)
|
||||
{
|
||||
columnTokenDecode[i] = ComputeDecoder(decoderRing[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return columnTokenDecode;
|
||||
|
||||
Func<uint, uint> ComputeDecoder(MetadataTable[] decoderData)
|
||||
{
|
||||
Func<uint, uint> result;
|
||||
|
||||
if (decoderData.Length == 1)
|
||||
{
|
||||
MetadataTable metadataTable = decoderData[0];
|
||||
result = delegate (uint input) { return CreateToken(metadataTable, input); };
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((decoderData.Length == 1) && decoderData[0] == (MetadataTable)(-2))
|
||||
{
|
||||
MetadataTable metadataTable = decoderData[0];
|
||||
if (!ptrTablesPresent.Contains(decoderData[2]))
|
||||
{
|
||||
metadataTable = decoderData[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
metadataTable = decoderData[2];
|
||||
}
|
||||
result = delegate (uint input) { return CreateToken(metadataTable, input); };
|
||||
}
|
||||
else
|
||||
{
|
||||
result = delegate (uint input) { return DecodeCodedIndex(input, decoderData); };
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// 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.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Diagnostics.DataContractReader.Contracts;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Helpers;
|
||||
|
||||
internal partial class EcmaMetadataReader
|
||||
{
|
||||
public static MetadataTable TokenToTable(uint token)
|
||||
{
|
||||
byte tableIndex = (byte)(token >> 24);
|
||||
if (tableIndex > (uint)MetadataTable.GenericParamConstraint)
|
||||
{
|
||||
return MetadataTable.Unused;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (MetadataTable)tableIndex;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsSigned<T>() where T : struct, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
return T.IsNegative(T.MinValue);
|
||||
}
|
||||
private static bool TryReadCore<T>(ReadOnlySpan<byte> bytes, out T value) where T : struct, IBinaryInteger<T>, IMinMaxValue<T>
|
||||
{
|
||||
return T.TryReadLittleEndian(bytes.Slice(0, Unsafe.SizeOf<T>()), IsSigned<T>(), out value);
|
||||
}
|
||||
|
||||
private static T ReadLittleEndian<T>(ReadOnlySpan<byte> bytes) where T : struct, IBinaryInteger<T>, IMinMaxValue<T>
|
||||
{
|
||||
if (!TryReadCore<T>(bytes, out T value))
|
||||
throw new ArgumentOutOfRangeException(nameof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
public static uint RidFromToken(uint token)
|
||||
{
|
||||
return token & 0xFFFFFF;
|
||||
}
|
||||
public static uint CreateToken(MetadataTable table, uint rid)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan<uint>(rid, 0xFFFFFF, nameof(rid));
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan<int>((int)table, (int)MetadataTable.GenericParamConstraint, nameof(table));
|
||||
return ((uint)table << 24) | rid;
|
||||
}
|
||||
|
||||
}
|
399
src/native/managed/cdacreader/src/Helpers/EcmaMetadataReader.cs
Normal file
399
src/native/managed/cdacreader/src/Helpers/EcmaMetadataReader.cs
Normal file
|
@ -0,0 +1,399 @@
|
|||
// 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.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Diagnostics.DataContractReader.Contracts;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Helpers;
|
||||
|
||||
internal struct EcmaMetadataCursor
|
||||
{
|
||||
internal ReadOnlyMemory<byte> TableData;
|
||||
internal MetadataTable Table;
|
||||
internal uint Rid;
|
||||
internal int RowSize;
|
||||
|
||||
public ReadOnlySpan<byte> Row
|
||||
{
|
||||
get
|
||||
{
|
||||
return TableData.Span.Slice((int)(RowSize * (Rid - 1)), (int)RowSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal partial class EcmaMetadataReader
|
||||
{
|
||||
private EcmaMetadata _ecmaMetadata;
|
||||
private int[] rowSize;
|
||||
private int[] columnSize;
|
||||
private int[] columnOffset;
|
||||
private Func<uint, uint>[] columnTokenDecode;
|
||||
|
||||
|
||||
public EcmaMetadataReader(ReadOnlyMemory<byte> imageMemory)
|
||||
{
|
||||
columnSize = new int[(int)MetadataColumnIndex.Count];
|
||||
columnOffset = new int[(int)MetadataColumnIndex.Count];
|
||||
rowSize = new int[(int)MetadataTable.Count];
|
||||
columnTokenDecode = Array.Empty<Func<uint, uint>>();
|
||||
|
||||
ReadOnlySpan<byte> image = imageMemory.Span;
|
||||
int magic = ReadLittleEndian<int>(image);
|
||||
if (magic != 0x424A5342)
|
||||
throw new ArgumentOutOfRangeException(nameof(imageMemory));
|
||||
|
||||
int versionSize = ReadLittleEndian<int>(image.Slice(12, 4));
|
||||
versionSize = AlignUp(versionSize, 4);
|
||||
|
||||
ReadOnlySpan<byte> versionName = image.Slice(16, versionSize);
|
||||
int nullTerminatorIndex = versionName.IndexOf((byte)0);
|
||||
|
||||
if ((nullTerminatorIndex == -1) || (nullTerminatorIndex == 0))
|
||||
{
|
||||
// VersionName isn't null terminated
|
||||
throw new ArgumentException(nameof(imageMemory));
|
||||
}
|
||||
|
||||
string metadataVersion = Encoding.UTF8.GetString(versionName.Slice(0, nullTerminatorIndex));
|
||||
|
||||
int currentOffset = 16 + versionSize;
|
||||
|
||||
currentOffset += 2; // Flags ... unused in this implementation
|
||||
ushort streams = ReadLittleEndian<ushort>(image.Slice(currentOffset));
|
||||
currentOffset += 2;
|
||||
|
||||
ReadOnlyMemory<byte> StringHeap = null;
|
||||
ReadOnlyMemory<byte> UserStringHeap = null;
|
||||
ReadOnlyMemory<byte> BlobHeap = null;
|
||||
ReadOnlyMemory<byte> GuidHeap = null;
|
||||
ReadOnlyMemory<byte> TablesHeap = null;
|
||||
|
||||
for (ushort iStream = 0; iStream < streams; iStream++)
|
||||
{
|
||||
var stream = ReadStream(ref image);
|
||||
if (stream.Name == "#Strings")
|
||||
{
|
||||
StringHeap = stream.Data;
|
||||
}
|
||||
else if (stream.Name == "#US")
|
||||
{
|
||||
UserStringHeap = stream.Data;
|
||||
}
|
||||
else if (stream.Name == "#Blob")
|
||||
{
|
||||
BlobHeap = stream.Data;
|
||||
}
|
||||
else if (stream.Name == "#GUID")
|
||||
{
|
||||
GuidHeap = stream.Data;
|
||||
}
|
||||
else if (stream.Name == "#~")
|
||||
{
|
||||
TablesHeap = stream.Data;
|
||||
}
|
||||
else if (stream.Name == "#-")
|
||||
{
|
||||
TablesHeap = stream.Data;
|
||||
}
|
||||
}
|
||||
|
||||
if (TablesHeap.Length == 0)
|
||||
{
|
||||
throw new ArgumentException(nameof(imageMemory));
|
||||
}
|
||||
ReadOnlySpan<byte> tables = TablesHeap.Span;
|
||||
|
||||
byte heapSizes = ReadLittleEndian<byte>(tables.Slice(6, 1));
|
||||
ulong validTables = ReadLittleEndian<ulong>(tables.Slice(8, 8));
|
||||
ulong sortedTables = ReadLittleEndian<ulong>(tables.Slice(16, 8));
|
||||
|
||||
int[] tableRowCounts = new int[(int)MetadataTable.Count];
|
||||
bool[] isSorted = new bool[(int)MetadataTable.Count];
|
||||
int currentTablesOffset = 24;
|
||||
for (int i = 0; i < (int)MetadataTable.Count; i++)
|
||||
{
|
||||
if ((validTables & ((ulong)1 << i)) != 0)
|
||||
{
|
||||
tableRowCounts[i] = ReadLittleEndian<int>(tables.Slice(currentTablesOffset));
|
||||
currentTablesOffset += 4;
|
||||
}
|
||||
if ((sortedTables & ((ulong)1 << i)) != 0)
|
||||
{
|
||||
isSorted[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// There is an undocumented flag "extra_data" which adds a 4 byte pad here.
|
||||
if ((heapSizes & 0x40) != 0)
|
||||
{
|
||||
currentTablesOffset += 4;
|
||||
}
|
||||
|
||||
EcmaMetadataSchema schema = new EcmaMetadataSchema(metadataVersion,
|
||||
largeStringHeap: (heapSizes & 1) != 0,
|
||||
largeGuidHeap: (heapSizes & 2) != 0,
|
||||
largeBlobHeap: (heapSizes & 4) != 0,
|
||||
rowCount: tableRowCounts,
|
||||
isSorted: isSorted,
|
||||
variableSizedColumnsAre4BytesLong: false
|
||||
);
|
||||
|
||||
ReadOnlyMemory<byte>[] tableData = new ReadOnlyMemory<byte>[(int)MetadataTable.Count];
|
||||
|
||||
_ecmaMetadata = new EcmaMetadata(schema, tableData, StringHeap, UserStringHeap, BlobHeap, GuidHeap);
|
||||
|
||||
Init();
|
||||
|
||||
// Init will compute row sizes, which is necessary for actually computing the tableData
|
||||
|
||||
for (int i = 0; i < (int)MetadataTable.Count; i++)
|
||||
{
|
||||
checked
|
||||
{
|
||||
if ((validTables & ((ulong)1 << i)) != 0)
|
||||
{
|
||||
int tableSize = checked(rowSize![i] * _ecmaMetadata.Schema.RowCount[i]);
|
||||
tableData[i] = TablesHeap.Slice(currentTablesOffset, tableSize);
|
||||
currentTablesOffset += tableSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(string Name, ReadOnlyMemory<byte> Data) ReadStream(ref ReadOnlySpan<byte> image)
|
||||
{
|
||||
int offset = ReadLittleEndian<int>(image.Slice(currentOffset));
|
||||
currentOffset += 4;
|
||||
int size = ReadLittleEndian<int>(image.Slice(currentOffset));
|
||||
currentOffset += 4;
|
||||
int nameStartOffset = currentOffset;
|
||||
int nameLen = 0;
|
||||
while (image[currentOffset++] != 0)
|
||||
{
|
||||
nameLen++;
|
||||
if (nameLen > 31) throw new ArgumentException(nameof(imageMemory));
|
||||
}
|
||||
|
||||
if (nameLen == 0) throw new ArgumentException(nameof(imageMemory));
|
||||
|
||||
currentOffset = AlignUp(currentOffset, 4);
|
||||
return (Encoding.ASCII.GetString(image.Slice(nameStartOffset, nameLen)), imageMemory.Slice(offset, size));
|
||||
}
|
||||
}
|
||||
|
||||
private static int AlignUp(int input, int alignment)
|
||||
{
|
||||
return input + (alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
public EcmaMetadataReader(EcmaMetadata ecmaMetadata)
|
||||
{
|
||||
columnTokenDecode = Array.Empty<Func<uint, uint>>();
|
||||
columnSize = new int[(int)MetadataColumnIndex.Count];
|
||||
columnOffset = new int[(int)MetadataColumnIndex.Count];
|
||||
rowSize = new int[(int)MetadataTable.Count];
|
||||
|
||||
_ecmaMetadata = ecmaMetadata;
|
||||
Init();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
PtrTablesPresent ptrTable = PtrTablesPresent.None;
|
||||
if (RowCount(MetadataTable.MethodPtr) != 0)
|
||||
ptrTable |= PtrTablesPresent.Method;
|
||||
if (RowCount(MetadataTable.FieldPtr) != 0)
|
||||
ptrTable |= PtrTablesPresent.Field;
|
||||
if (RowCount(MetadataTable.ParamPtr) != 0)
|
||||
ptrTable |= PtrTablesPresent.Param;
|
||||
if (RowCount(MetadataTable.EventPtr) != 0)
|
||||
ptrTable |= PtrTablesPresent.Event;
|
||||
if (RowCount(MetadataTable.PropertyPtr) != 0)
|
||||
ptrTable |= PtrTablesPresent.Property;
|
||||
|
||||
columnTokenDecode = columnTokenDecoders[(int)ptrTable];
|
||||
|
||||
ComputeColumnSizesAndOffsets();
|
||||
|
||||
void ComputeColumnSizesAndOffsets()
|
||||
{
|
||||
MetadataTable currentTable = MetadataTable.Unused;
|
||||
MetadataColumnIndex? prevColumn = null;
|
||||
|
||||
for (int i = 0; i < (int)MetadataColumnIndex.Count; i++)
|
||||
{
|
||||
MetadataColumnIndex column = (MetadataColumnIndex)i;
|
||||
MetadataTable newColumnTable = ColumnTable(column);
|
||||
if (currentTable != newColumnTable)
|
||||
{
|
||||
if (prevColumn.HasValue)
|
||||
rowSize[(int)currentTable] = ComputeColumnEnd(prevColumn.Value);
|
||||
currentTable = newColumnTable;
|
||||
columnOffset[i] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
columnOffset[i] = ComputeColumnEnd(prevColumn!.Value);
|
||||
}
|
||||
prevColumn = column;
|
||||
|
||||
columnSize[i] = columnTypes[i] switch
|
||||
{
|
||||
ColumnType.TwoByteConstant => 2,
|
||||
ColumnType.FourByteConstant => 4,
|
||||
ColumnType.Utf8String => _ecmaMetadata.Schema.LargeStringHeap ? 4 : 2,
|
||||
ColumnType.Blob => _ecmaMetadata.Schema.LargeBlobHeap ? 4 : 2,
|
||||
ColumnType.Guid => _ecmaMetadata.Schema.LargeGuidHeap ? 4 : 2,
|
||||
ColumnType.Token => _ecmaMetadata.Schema.VariableSizedColumnsAreAll4BytesLong ? 4 : CodedIndexEncodingBytes(codedIndexDecoderRing[i]),
|
||||
_ => throw new System.Exception()
|
||||
};
|
||||
}
|
||||
|
||||
rowSize[(int)ColumnTable(prevColumn!.Value)] = ComputeColumnEnd(prevColumn!.Value);
|
||||
}
|
||||
|
||||
int ComputeColumnEnd(MetadataColumnIndex column)
|
||||
{
|
||||
return ColumnOffset(column) + ColumnSize(column);
|
||||
}
|
||||
}
|
||||
|
||||
public EcmaMetadata UnderlyingMetadata => _ecmaMetadata;
|
||||
|
||||
public uint GetColumnAsConstant(EcmaMetadataCursor c, MetadataColumnIndex col_idx)
|
||||
{
|
||||
if (columnTypes[(int)col_idx] != ColumnType.TwoByteConstant && columnTypes[(int)col_idx] != ColumnType.FourByteConstant)
|
||||
throw new ArgumentOutOfRangeException(nameof(col_idx));
|
||||
return GetColumnRaw(c, col_idx);
|
||||
}
|
||||
|
||||
public System.ReadOnlySpan<byte> GetColumnAsBlob(EcmaMetadataCursor c, MetadataColumnIndex col_idx)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public uint GetColumnAsToken(EcmaMetadataCursor c, MetadataColumnIndex col_idx)
|
||||
{
|
||||
Func<uint, uint> decoder = columnTokenDecode[(int)col_idx];
|
||||
if (decoder == null)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(col_idx));
|
||||
}
|
||||
uint rawResult = GetColumnRaw(c, col_idx);
|
||||
uint result = decoder(rawResult);
|
||||
return result;
|
||||
}
|
||||
|
||||
public System.ReadOnlySpan<byte> GetColumnAsUtf8(EcmaMetadataCursor c, MetadataColumnIndex col_idx)
|
||||
{
|
||||
if (columnTypes[(int)col_idx] != ColumnType.Utf8String)
|
||||
throw new ArgumentOutOfRangeException(nameof(col_idx));
|
||||
int initialOffset = (int)GetColumnRaw(c, col_idx);
|
||||
|
||||
if (initialOffset == 0)
|
||||
return default(ReadOnlySpan<byte>);
|
||||
|
||||
checked
|
||||
{
|
||||
ReadOnlySpan<byte> stringHeap = _ecmaMetadata.StringHeap.Span;
|
||||
int curOffset = initialOffset;
|
||||
while (stringHeap[curOffset] != '\0')
|
||||
{
|
||||
curOffset++;
|
||||
}
|
||||
return stringHeap.Slice(initialOffset, curOffset - initialOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetCursor(uint token, out EcmaMetadataCursor cursor)
|
||||
{
|
||||
cursor = default;
|
||||
MetadataTable table = TokenToTable(token);
|
||||
if (table == MetadataTable.Unused)
|
||||
return false;
|
||||
|
||||
if (RowCount(table) < RidFromToken(token))
|
||||
return false;
|
||||
|
||||
cursor.Rid = RidFromToken(token);
|
||||
cursor.TableData = _ecmaMetadata.Tables[(int)table];
|
||||
cursor.RowSize = rowSize[(int)table];
|
||||
cursor.Table = table;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetCursorToFirstEntryInTable(MetadataTable table, out EcmaMetadataCursor cursor)
|
||||
{
|
||||
cursor = default;
|
||||
if (RowCount(table) > 0)
|
||||
{
|
||||
cursor.Rid = 1;
|
||||
cursor.TableData = _ecmaMetadata.Tables[(int)table];
|
||||
cursor.RowSize = rowSize[(int)table];
|
||||
cursor.Table = table;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryFindRowFromCursor(EcmaMetadataCursor tableCursor, MetadataColumnIndex col_idx, uint searchToken, out EcmaMetadataCursor foundRow)
|
||||
{
|
||||
foundRow = tableCursor;
|
||||
|
||||
/* if (_ecmaMetadata.Schema.IsSorted[(int)tableCursor.Table])
|
||||
{
|
||||
// TODO(cdac) implement sorted searching in metadata
|
||||
}
|
||||
else*/
|
||||
{
|
||||
while (foundRow.Rid <= RowCount(tableCursor.Table))
|
||||
{
|
||||
if (GetColumnAsToken(foundRow, col_idx) == searchToken)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
foundRow.Rid += 1;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public EcmaMetadataCursor GetCursor(uint token)
|
||||
{
|
||||
if (!TryGetCursor(token, out EcmaMetadataCursor cursor))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(token));
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public static uint GetToken(EcmaMetadataCursor c)
|
||||
{
|
||||
return CreateToken(c.Table, c.Rid);
|
||||
}
|
||||
|
||||
private static MetadataTable ColumnTable(MetadataColumnIndex column)
|
||||
{
|
||||
return columnTable[(int)column];
|
||||
}
|
||||
|
||||
public virtual string GetColumnAsUtf8String(EcmaMetadataCursor c, MetadataColumnIndex col_idx)
|
||||
{
|
||||
ReadOnlySpan<byte> utf8Data = GetColumnAsUtf8(c, col_idx);
|
||||
string str = string.Empty;
|
||||
if (utf8Data.Length > 0)
|
||||
{
|
||||
str = System.Text.Encoding.UTF8.GetString(utf8Data);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
334
src/native/managed/cdacreader/src/Helpers/Metadata.cs
Normal file
334
src/native/managed/cdacreader/src/Helpers/Metadata.cs
Normal file
|
@ -0,0 +1,334 @@
|
|||
// 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 Microsoft.Diagnostics.DataContractReader.Contracts;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Helpers;
|
||||
|
||||
internal enum MetadataTable
|
||||
{
|
||||
Unused = -1,
|
||||
Module = 0x0,
|
||||
TypeRef = 0x01,
|
||||
TypeDef = 0x02,
|
||||
FieldPtr = 0x03,
|
||||
Field = 0x04,
|
||||
MethodPtr = 0x05,
|
||||
MethodDef = 0x06,
|
||||
ParamPtr = 0x07,
|
||||
Param = 0x08,
|
||||
InterfaceImpl = 0x09,
|
||||
MemberRef = 0x0a,
|
||||
Constant = 0x0b,
|
||||
CustomAttribute = 0x0c,
|
||||
FieldMarshal = 0x0d,
|
||||
DeclSecurity = 0x0e,
|
||||
ClassLayout = 0x0f,
|
||||
FieldLayout = 0x10,
|
||||
StandAloneSig = 0x11,
|
||||
EventMap = 0x12,
|
||||
EventPtr = 0x13,
|
||||
Event = 0x14,
|
||||
PropertyMap = 0x15,
|
||||
PropertyPtr = 0x16,
|
||||
Property = 0x17,
|
||||
MethodSemantics = 0x18,
|
||||
MethodImpl = 0x19,
|
||||
ModuleRef = 0x1a,
|
||||
TypeSpec = 0x1b,
|
||||
ImplMap = 0x1c,
|
||||
FieldRva = 0x1d,
|
||||
ENCLog = 0x1e,
|
||||
ENCMap = 0x1f,
|
||||
Assembly = 0x20,
|
||||
AssemblyProcessor = 0x21,
|
||||
AssemblyOS = 0x22,
|
||||
AssemblyRef = 0x23,
|
||||
AssemblyRefProcessor = 0x24,
|
||||
AssemblyRefOS = 0x25,
|
||||
File = 0x26,
|
||||
ExportedType = 0x27,
|
||||
ManifestResource = 0x28,
|
||||
NestedClass = 0x29,
|
||||
GenericParam = 0x2a,
|
||||
MethodSpec = 0x2b,
|
||||
GenericParamConstraint = 0x2c,
|
||||
Count = 0x2d
|
||||
}
|
||||
|
||||
internal class EcmaMetadata
|
||||
{
|
||||
public EcmaMetadata(EcmaMetadataSchema schema,
|
||||
ReadOnlyMemory<byte>[] tables,
|
||||
ReadOnlyMemory<byte> stringHeap,
|
||||
ReadOnlyMemory<byte> userStringHeap,
|
||||
ReadOnlyMemory<byte> blobHeap,
|
||||
ReadOnlyMemory<byte> guidHeap)
|
||||
{
|
||||
Schema = schema;
|
||||
_tables = tables;
|
||||
StringHeap = stringHeap;
|
||||
UserStringHeap = userStringHeap;
|
||||
BlobHeap = blobHeap;
|
||||
GuidHeap = guidHeap;
|
||||
}
|
||||
|
||||
public EcmaMetadataSchema Schema { get; init; }
|
||||
|
||||
private ReadOnlyMemory<byte>[] _tables;
|
||||
public ReadOnlySpan<ReadOnlyMemory<byte>> Tables => _tables;
|
||||
public ReadOnlyMemory<byte> StringHeap { get; init; }
|
||||
public ReadOnlyMemory<byte> UserStringHeap { get; init; }
|
||||
public ReadOnlyMemory<byte> BlobHeap { get; init; }
|
||||
public ReadOnlyMemory<byte> GuidHeap { get; init; }
|
||||
|
||||
private Microsoft.Diagnostics.DataContractReader.Helpers.EcmaMetadataReader? _ecmaMetadataReader;
|
||||
public Microsoft.Diagnostics.DataContractReader.Helpers.EcmaMetadataReader EcmaMetadataReader
|
||||
{
|
||||
get
|
||||
{
|
||||
_ecmaMetadataReader ??= new Helpers.EcmaMetadataReader(this);
|
||||
return _ecmaMetadataReader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal enum MetadataColumnIndex
|
||||
{
|
||||
Module_Generation,
|
||||
Module_Name,
|
||||
Module_Mvid,
|
||||
Module_EncId,
|
||||
Module_EncBaseId,
|
||||
|
||||
TypeRef_ResolutionScope,
|
||||
TypeRef_TypeName,
|
||||
TypeRef_TypeNamespace,
|
||||
|
||||
TypeDef_Flags,
|
||||
TypeDef_TypeName,
|
||||
TypeDef_TypeNamespace,
|
||||
TypeDef_Extends,
|
||||
TypeDef_FieldList,
|
||||
TypeDef_MethodList,
|
||||
|
||||
FieldPtr_Field,
|
||||
|
||||
Field_Flags,
|
||||
Field_Name,
|
||||
Field_Signature,
|
||||
|
||||
MethodPtr_Method,
|
||||
|
||||
MethodDef_Rva,
|
||||
MethodDef_ImplFlags,
|
||||
MethodDef_Flags,
|
||||
MethodDef_Name,
|
||||
MethodDef_Signature,
|
||||
MethodDef_ParamList,
|
||||
|
||||
ParamPtr_Param,
|
||||
|
||||
Param_Flags,
|
||||
Param_Sequence,
|
||||
Param_Name,
|
||||
|
||||
InterfaceImpl_Class,
|
||||
InterfaceImpl_Interface,
|
||||
|
||||
MemberRef_Class,
|
||||
MemberRef_Name,
|
||||
MemberRef_Signature,
|
||||
|
||||
Constant_Type,
|
||||
Constant_Parent,
|
||||
Constant_Value,
|
||||
|
||||
CustomAttribute_Parent,
|
||||
CustomAttribute_Type,
|
||||
CustomAttribute_Value,
|
||||
|
||||
FieldMarshal_Parent,
|
||||
FieldMarshal_NativeType,
|
||||
|
||||
DeclSecurity_Action,
|
||||
DeclSecurity_Parent,
|
||||
DeclSecurity_PermissionSet,
|
||||
|
||||
ClassLayout_PackingSize,
|
||||
ClassLayout_ClassSize,
|
||||
ClassLayout_Parent,
|
||||
|
||||
FieldLayout_Offset,
|
||||
FieldLayout_Field,
|
||||
|
||||
StandAloneSig_Signature,
|
||||
|
||||
EventMap_Parent,
|
||||
EventMap_EventList,
|
||||
|
||||
EventPtr_Event,
|
||||
|
||||
Event_EventFlags,
|
||||
Event_Name,
|
||||
Event_EventType,
|
||||
|
||||
PropertyMap_Parent,
|
||||
PropertyMap_PropertyList,
|
||||
|
||||
PropertyPtr_Property,
|
||||
|
||||
Property_Flags,
|
||||
Property_Name,
|
||||
Property_Type,
|
||||
|
||||
MethodSemantics_Semantics,
|
||||
MethodSemantics_Method,
|
||||
MethodSemantics_Association,
|
||||
|
||||
MethodImpl_Class,
|
||||
MethodImpl_MethodBody,
|
||||
MethodImpl_MethodDeclaration,
|
||||
|
||||
ModuleRef_Name,
|
||||
|
||||
TypeSpec_Signature,
|
||||
|
||||
ImplMap_MappingFlags,
|
||||
ImplMap_MemberForwarded,
|
||||
ImplMap_ImportName,
|
||||
ImplMap_ImportScope,
|
||||
|
||||
FieldRva_Rva,
|
||||
FieldRva_Field,
|
||||
|
||||
ENCLog_Token,
|
||||
ENCLog_Op,
|
||||
|
||||
ENCMap_Token,
|
||||
|
||||
Assembly_HashAlgId,
|
||||
Assembly_MajorVersion,
|
||||
Assembly_MinorVersion,
|
||||
Assembly_BuildNumber,
|
||||
Assembly_RevisionNumber,
|
||||
Assembly_Flags,
|
||||
Assembly_PublicKey,
|
||||
Assembly_Name,
|
||||
Assembly_Culture,
|
||||
|
||||
AssemblyRef_MajorVersion,
|
||||
AssemblyRef_MinorVersion,
|
||||
AssemblyRef_BuildNumber,
|
||||
AssemblyRef_RevisionNumber,
|
||||
AssemblyRef_Flags,
|
||||
AssemblyRef_PublicKeyOrToken,
|
||||
AssemblyRef_Name,
|
||||
AssemblyRef_Culture,
|
||||
AssemblyRef_HashValue,
|
||||
|
||||
File_Flags,
|
||||
File_Name,
|
||||
File_HashValue,
|
||||
|
||||
ExportedType_Flags,
|
||||
ExportedType_TypeDefId,
|
||||
ExportedType_TypeName,
|
||||
ExportedType_TypeNamespace,
|
||||
ExportedType_Implementation,
|
||||
|
||||
ManifestResource_Offset,
|
||||
ManifestResource_Flags,
|
||||
ManifestResource_Name,
|
||||
ManifestResource_Implementation,
|
||||
|
||||
NestedClass_NestedClass,
|
||||
NestedClass_EnclosingClass,
|
||||
|
||||
GenericParam_Number,
|
||||
GenericParam_Flags,
|
||||
GenericParam_Owner,
|
||||
GenericParam_Name,
|
||||
|
||||
MethodSpec_Method,
|
||||
MethodSpec_Instantiation,
|
||||
|
||||
GenericParamConstraint_Owner,
|
||||
GenericParamConstraint_Constraint,
|
||||
|
||||
Count
|
||||
}
|
||||
|
||||
internal class Metadata
|
||||
{
|
||||
private readonly Target _target;
|
||||
private readonly Dictionary<ulong, EcmaMetadata> _metadata = [];
|
||||
|
||||
public Metadata(Target target)
|
||||
{
|
||||
_target = target;
|
||||
}
|
||||
|
||||
public virtual EcmaMetadata GetMetadata(Contracts.ModuleHandle module)
|
||||
{
|
||||
if (_metadata.TryGetValue(module.Address, out EcmaMetadata? result))
|
||||
return result;
|
||||
|
||||
AvailableMetadataType metadataType = _target.Contracts.Loader.GetAvailableMetadataType(module);
|
||||
|
||||
if (metadataType == AvailableMetadataType.ReadOnly)
|
||||
{
|
||||
if (this.MetadataProvider != null)
|
||||
result = this.MetadataProvider(module);
|
||||
if (result == null)
|
||||
{
|
||||
TargetPointer address = _target.Contracts.Loader.GetMetadataAddress(module, out ulong size);
|
||||
byte[] data = new byte[size];
|
||||
_target.ReadBuffer(address, data);
|
||||
result = (new Helpers.EcmaMetadataReader(new ReadOnlyMemory<byte>(data))).UnderlyingMetadata;
|
||||
}
|
||||
}
|
||||
else if (metadataType == AvailableMetadataType.ReadWriteSavedCopy)
|
||||
{
|
||||
TargetPointer address = _target.Contracts.Loader.GetReadWriteSavedMetadataAddress(module, out ulong size);
|
||||
byte[] data = new byte[size];
|
||||
_target.ReadBuffer(address, data);
|
||||
result = (new Helpers.EcmaMetadataReader(new ReadOnlyMemory<byte>(data))).UnderlyingMetadata;
|
||||
}
|
||||
else
|
||||
{
|
||||
var targetEcmaMetadata = _target.Contracts.Loader.GetReadWriteMetadata(module);
|
||||
result = new EcmaMetadata(targetEcmaMetadata.Schema,
|
||||
GetReadOnlyMemoryFromTargetSpans(targetEcmaMetadata.Tables),
|
||||
GetReadOnlyMemoryFromTargetSpan(targetEcmaMetadata.StringHeap),
|
||||
GetReadOnlyMemoryFromTargetSpan(targetEcmaMetadata.UserStringHeap),
|
||||
GetReadOnlyMemoryFromTargetSpan(targetEcmaMetadata.BlobHeap),
|
||||
GetReadOnlyMemoryFromTargetSpan(targetEcmaMetadata.GuidHeap));
|
||||
|
||||
ReadOnlyMemory<byte> GetReadOnlyMemoryFromTargetSpan(TargetSpan span)
|
||||
{
|
||||
if (span.Size == 0)
|
||||
return default;
|
||||
byte[] data = new byte[span.Size];
|
||||
_target.ReadBuffer(span.Address, data);
|
||||
return new ReadOnlyMemory<byte>(data);
|
||||
}
|
||||
ReadOnlyMemory<byte>[] GetReadOnlyMemoryFromTargetSpans(ReadOnlySpan<TargetSpan> spans)
|
||||
{
|
||||
ReadOnlyMemory<byte>[] memories = new ReadOnlyMemory<byte>[spans.Length];
|
||||
for (int i = 0; i < spans.Length; i++)
|
||||
{
|
||||
memories[i] = GetReadOnlyMemoryFromTargetSpan(spans[i]);
|
||||
}
|
||||
return memories;
|
||||
}
|
||||
}
|
||||
|
||||
_metadata.Add(module.Address, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Func<Contracts.ModuleHandle, EcmaMetadata>? MetadataProvider;
|
||||
}
|
|
@ -1,7 +1,11 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using Microsoft.Diagnostics.DataContractReader.Contracts;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
|
||||
|
@ -89,7 +93,7 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface2,
|
|||
try
|
||||
{
|
||||
Contracts.IRuntimeTypeSystem contract = _target.Contracts.RuntimeTypeSystem;
|
||||
Contracts.MethodTableHandle methodTable = contract.GetMethodTableHandle(mt);
|
||||
Contracts.TypeHandle methodTable = contract.GetTypeHandle(mt);
|
||||
|
||||
DacpMethodTableData result = default;
|
||||
result.baseSize = contract.GetBaseSize(methodTable);
|
||||
|
@ -123,7 +127,7 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface2,
|
|||
*data = result;
|
||||
return HResults.S_OK;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
return ex.HResult;
|
||||
}
|
||||
|
@ -137,16 +141,78 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface2,
|
|||
try
|
||||
{
|
||||
Contracts.IRuntimeTypeSystem contract = _target.Contracts.RuntimeTypeSystem;
|
||||
Contracts.MethodTableHandle methodTableHandle = contract.GetMethodTableHandle(eeClassReallyCanonMT);
|
||||
Contracts.TypeHandle methodTableHandle = contract.GetTypeHandle(eeClassReallyCanonMT);
|
||||
*value = methodTableHandle.Address;
|
||||
return HResults.S_OK;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (global::System.Exception ex)
|
||||
{
|
||||
return ex.HResult;
|
||||
}
|
||||
}
|
||||
public unsafe int GetMethodTableName(ulong mt, uint count, char* mtName, uint* pNeeded) => HResults.E_NOTIMPL;
|
||||
|
||||
private unsafe void CopyStringToTargetBuffer(char* stringBuf, uint bufferSize, uint* neededBufferSize, string str)
|
||||
{
|
||||
ReadOnlySpan<char> strSpan = str.AsSpan();
|
||||
if (neededBufferSize != null)
|
||||
*neededBufferSize = checked((uint)(strSpan.Length + 1));
|
||||
|
||||
if (stringBuf != null && bufferSize > 0)
|
||||
{
|
||||
Span<char> target = new Span<char>(stringBuf, checked((int)bufferSize));
|
||||
int nullTerminatorLocation = strSpan.Length > bufferSize - 1 ? checked((int)(bufferSize - 1)) : strSpan.Length;
|
||||
strSpan = strSpan.Slice(0, nullTerminatorLocation);
|
||||
strSpan.CopyTo(target);
|
||||
target[nullTerminatorLocation] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe int GetMethodTableName(ulong mt, uint count, char* mtName, uint* pNeeded)
|
||||
{
|
||||
if (mt == 0)
|
||||
return HResults.E_INVALIDARG;
|
||||
|
||||
try
|
||||
{
|
||||
Contracts.IRuntimeTypeSystem typeSystemContract = _target.Contracts.RuntimeTypeSystem;
|
||||
Contracts.TypeHandle methodTableHandle = typeSystemContract.GetTypeHandle(mt);
|
||||
if (typeSystemContract.IsFreeObjectMethodTable(methodTableHandle))
|
||||
{
|
||||
CopyStringToTargetBuffer(mtName, count, pNeeded, "Free");
|
||||
return HResults.S_OK;
|
||||
}
|
||||
|
||||
// TODO(cdac) - The original code handles the case of the module being in the process of being unloaded. This is not yet handled
|
||||
|
||||
System.Text.StringBuilder methodTableName = new();
|
||||
try
|
||||
{
|
||||
TargetPointer modulePointer = typeSystemContract.GetModule(methodTableHandle);
|
||||
TypeNameBuilder.AppendType(_target, methodTableName, methodTableHandle, TypeNameFormat.FormatNamespace | TypeNameFormat.FormatFullInst);
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
string? fallbackName = _target.Contracts.DacStreams.StringFromEEAddress(mt);
|
||||
if (fallbackName != null)
|
||||
{
|
||||
methodTableName.Clear();
|
||||
methodTableName.Append(fallbackName);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
}
|
||||
CopyStringToTargetBuffer(mtName, count, pNeeded, methodTableName.ToString());
|
||||
return HResults.S_OK;
|
||||
}
|
||||
catch (global::System.Exception ex)
|
||||
{
|
||||
return ex.HResult;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe int GetMethodTableSlot(ulong mt, uint slot, ulong* value) => HResults.E_NOTIMPL;
|
||||
public unsafe int GetMethodTableTransparencyData(ulong mt, void* data) => HResults.E_NOTIMPL;
|
||||
public unsafe int GetModule(ulong addr, void** mod) => HResults.E_NOTIMPL;
|
||||
|
@ -191,7 +257,7 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface2,
|
|||
data->dwBaseClassIndex = 0;
|
||||
data->dwModuleIndex = 0;
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (global::System.Exception e)
|
||||
{
|
||||
return e.HResult;
|
||||
}
|
||||
|
@ -208,7 +274,7 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface2,
|
|||
*exceptionObject = exceptionObjectLocal;
|
||||
*nextNestedException = nextNestedExceptionLocal;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (global::System.Exception ex)
|
||||
{
|
||||
return ex.HResult;
|
||||
}
|
||||
|
@ -234,7 +300,7 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface2,
|
|||
data->HResult = exceptionData.HResult;
|
||||
data->XCode = exceptionData.XCode;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
return ex.HResult;
|
||||
}
|
||||
|
@ -284,7 +350,7 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface2,
|
|||
data->lastThrownObjectHandle = threadData.LastThrownObjectHandle;
|
||||
data->nextThread = threadData.NextThread;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (global::System.Exception ex)
|
||||
{
|
||||
return ex.HResult;
|
||||
}
|
||||
|
@ -314,7 +380,7 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface2,
|
|||
|
||||
data->fHostConfig = 0; // Always 0 for non-Framework
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (global::System.Exception ex)
|
||||
{
|
||||
return ex.HResult;
|
||||
}
|
||||
|
|
558
src/native/managed/cdacreader/src/Legacy/TypeNameBuilder.cs
Normal file
558
src/native/managed/cdacreader/src/Legacy/TypeNameBuilder.cs
Normal file
|
@ -0,0 +1,558 @@
|
|||
// 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.Text;
|
||||
using Microsoft.Diagnostics.DataContractReader.Contracts;
|
||||
using Microsoft.Diagnostics.DataContractReader.Helpers;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.Legacy;
|
||||
|
||||
[Flags]
|
||||
internal enum TypeNameFormat
|
||||
{
|
||||
FormatNamespace = 1,
|
||||
FormatFullInst = 2,
|
||||
FormatAngleBrackets = 4,
|
||||
FormatAssembly = 8,
|
||||
FormatGenericParam = 16,
|
||||
}
|
||||
|
||||
internal struct TypeNameBuilder
|
||||
{
|
||||
[Flags]
|
||||
private enum ParseState
|
||||
{
|
||||
Start = 1,
|
||||
Name = 2,
|
||||
GenArgs = 4,
|
||||
PtrArr = 8,
|
||||
ByRef = 16,
|
||||
AssemSpec = 32,
|
||||
Error
|
||||
}
|
||||
private StringBuilder TypeString;
|
||||
private Target Target;
|
||||
|
||||
private ParseState State;
|
||||
private bool UseAngleBracketsForGenerics { get; init; }
|
||||
private bool NestedName;
|
||||
private bool HasAssemblySpec;
|
||||
private bool FirstInstArg;
|
||||
private int InstNesting;
|
||||
private Stack<int>? GenericStartsStack;
|
||||
|
||||
private TypeNameBuilder(StringBuilder typeString, Target target, TypeNameFormat format)
|
||||
{
|
||||
TypeString = typeString;
|
||||
Target = target;
|
||||
UseAngleBracketsForGenerics = format.HasFlag(TypeNameFormat.FormatAngleBrackets);
|
||||
State = ParseState.Start;
|
||||
}
|
||||
|
||||
public static void AppendType(Target target, StringBuilder stringBuilder, Contracts.TypeHandle typeHandle, TypeNameFormat format)
|
||||
{
|
||||
AppendType(target, stringBuilder, typeHandle, default, format);
|
||||
}
|
||||
public static void AppendType(Target target, StringBuilder stringBuilder, Contracts.TypeHandle typeHandle, ReadOnlySpan<TypeHandle> typeInstantiation, TypeNameFormat format)
|
||||
{
|
||||
TypeNameBuilder builder = new(stringBuilder, target, format);
|
||||
AppendTypeCore(ref builder, typeHandle, typeInstantiation, format);
|
||||
}
|
||||
private static void AppendTypeCore(ref TypeNameBuilder tnb, Contracts.TypeHandle typeHandle, ReadOnlySpan<Contracts.TypeHandle> instantiation, TypeNameFormat format)
|
||||
{
|
||||
bool toString = format.HasFlag(TypeNameFormat.FormatNamespace) && !format.HasFlag(TypeNameFormat.FormatFullInst) && !format.HasFlag(TypeNameFormat.FormatAssembly);
|
||||
|
||||
if (typeHandle.IsNull)
|
||||
{
|
||||
tnb.AddName("(null)");
|
||||
}
|
||||
else
|
||||
{
|
||||
var typeSystemContract = tnb.Target.Contracts.RuntimeTypeSystem;
|
||||
if (typeSystemContract.HasTypeParam(typeHandle))
|
||||
{
|
||||
var elemType = typeSystemContract.GetSignatureCorElementType(typeHandle);
|
||||
if (elemType != Contracts.CorElementType.ValueType)
|
||||
{
|
||||
typeSystemContract.IsArray(typeHandle, out uint rank);
|
||||
AppendTypeCore(ref tnb, typeSystemContract.GetTypeParam(typeHandle), default(ReadOnlySpan<Contracts.TypeHandle>), (TypeNameFormat)(format & ~TypeNameFormat.FormatAssembly));
|
||||
AppendParamTypeQualifier(ref tnb, elemType, rank);
|
||||
}
|
||||
else
|
||||
{
|
||||
tnb.TypeString.Append("VALUETYPE");
|
||||
AppendTypeCore(ref tnb, typeSystemContract.GetTypeParam(typeHandle), Array.Empty<Contracts.TypeHandle>(), format & ~TypeNameFormat.FormatAssembly);
|
||||
}
|
||||
}
|
||||
else if (typeSystemContract.IsGenericVariable(typeHandle, out TargetPointer modulePointer, out uint genericParamToken))
|
||||
{
|
||||
Contracts.ModuleHandle module = tnb.Target.Contracts.Loader.GetModuleHandle(modulePointer);
|
||||
EcmaMetadataReader reader = tnb.Target.Metadata.GetMetadata(module).EcmaMetadataReader;
|
||||
EcmaMetadataCursor cursor = reader.GetCursor(genericParamToken);
|
||||
if (format.HasFlag(TypeNameFormat.FormatGenericParam))
|
||||
{
|
||||
uint owner = reader.GetColumnAsToken(cursor, MetadataColumnIndex.GenericParam_Owner);
|
||||
if (EcmaMetadataReader.TokenToTable(owner) == MetadataTable.TypeDef)
|
||||
{
|
||||
tnb.TypeString.Append('!');
|
||||
}
|
||||
else
|
||||
{
|
||||
tnb.TypeString.Append("!!");
|
||||
}
|
||||
}
|
||||
tnb.AddName(reader.GetColumnAsUtf8String(cursor, MetadataColumnIndex.GenericParam_Name));
|
||||
format &= ~TypeNameFormat.FormatAssembly;
|
||||
}
|
||||
else if (typeSystemContract.IsFunctionPointer(typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv))
|
||||
{
|
||||
if (format.HasFlag(TypeNameFormat.FormatNamespace))
|
||||
{
|
||||
StringBuilder stringBuilder = new();
|
||||
AppendType(tnb.Target, stringBuilder, retAndArgTypes[0], format);
|
||||
stringBuilder.Append('(');
|
||||
for (int i = 1; i < retAndArgTypes.Length; i++)
|
||||
{
|
||||
if (i != 1)
|
||||
{
|
||||
stringBuilder.Append(", ");
|
||||
}
|
||||
AppendType(tnb.Target, stringBuilder, retAndArgTypes[i], format);
|
||||
}
|
||||
|
||||
if ((callConv & 0x7) == 0x5) // Is this the VARARG calling convention?
|
||||
{
|
||||
if (retAndArgTypes.Length > 2)
|
||||
{
|
||||
stringBuilder.Append(", ");
|
||||
}
|
||||
stringBuilder.Append("...");
|
||||
}
|
||||
stringBuilder.Append(')');
|
||||
tnb.AddNameNoEscaping(stringBuilder);
|
||||
}
|
||||
else
|
||||
{
|
||||
tnb.AddNameNoEscaping(new StringBuilder());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...otherwise it's just a plain type def or an instantiated type
|
||||
uint typeDefToken = typeSystemContract.GetTypeDefToken(typeHandle);
|
||||
Contracts.ModuleHandle moduleHandle = tnb.Target.Contracts.Loader.GetModuleHandle(typeSystemContract.GetModule(typeHandle));
|
||||
if (EcmaMetadataReader.RidFromToken(typeDefToken) == 0)
|
||||
{
|
||||
tnb.AddName("(dynamicClass)");
|
||||
}
|
||||
else
|
||||
{
|
||||
EcmaMetadataReader reader = tnb.Target.Metadata.GetMetadata(moduleHandle).EcmaMetadataReader;
|
||||
AppendNestedTypeDef(ref tnb, reader, typeDefToken, format);
|
||||
}
|
||||
|
||||
// Append instantiation
|
||||
|
||||
if (format.HasFlag(TypeNameFormat.FormatNamespace) || format.HasFlag(TypeNameFormat.FormatAssembly))
|
||||
{
|
||||
ReadOnlySpan<TypeHandle> instantiationSpan = typeSystemContract.GetInstantiation(typeHandle);
|
||||
|
||||
if ((instantiationSpan.Length > 0) && (!typeSystemContract.IsGenericTypeDefinition(typeHandle) || toString))
|
||||
{
|
||||
if (instantiation.Length == 0)
|
||||
{
|
||||
instantiation = instantiationSpan;
|
||||
}
|
||||
AppendInst(ref tnb, instantiation, format);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (format.HasFlag(TypeNameFormat.FormatAssembly))
|
||||
{
|
||||
TargetPointer modulePtr = typeSystemContract.GetModule(typeHandle);
|
||||
|
||||
Contracts.ModuleHandle module = tnb.Target.Contracts.Loader.GetModuleHandle(modulePtr);
|
||||
// NOTE: The DAC variant of assembly name generation is different than the runtime version. The DAC variant is simpler, and only uses SimpleName
|
||||
EcmaMetadataReader mr = tnb.Target.Metadata.GetMetadata(module).EcmaMetadataReader;
|
||||
EcmaMetadataCursor cursor = mr.GetCursor(0x20000001);
|
||||
string assemblySimpleName = mr.GetColumnAsUtf8String(cursor, MetadataColumnIndex.Assembly_Name);
|
||||
|
||||
tnb.AddAssemblySpec(assemblySimpleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendInst(ref TypeNameBuilder tnb, ReadOnlySpan<TypeHandle> inst, TypeNameFormat format)
|
||||
{
|
||||
tnb.OpenGenericArguments();
|
||||
foreach (TypeHandle arg in inst)
|
||||
{
|
||||
tnb.OpenGenericArgument();
|
||||
if (format.HasFlag(TypeNameFormat.FormatFullInst) && !tnb.Target.Contracts.RuntimeTypeSystem.IsGenericVariable(arg, out _, out _))
|
||||
{
|
||||
AppendTypeCore(ref tnb, arg, default, format | TypeNameFormat.FormatNamespace | TypeNameFormat.FormatAssembly);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendTypeCore(ref tnb, arg, default, format & (TypeNameFormat.FormatNamespace | TypeNameFormat.FormatAngleBrackets));
|
||||
}
|
||||
tnb.CloseGenericArgument();
|
||||
}
|
||||
tnb.CloseGenericArguments();
|
||||
}
|
||||
|
||||
private void OpenGenericArguments()
|
||||
{
|
||||
if (!CheckParseState(ParseState.Name))
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
State = ParseState.Start;
|
||||
InstNesting++;
|
||||
FirstInstArg = true;
|
||||
|
||||
TypeString.Append(UseAngleBracketsForGenerics ? '<' : '[');
|
||||
}
|
||||
|
||||
private void OpenGenericArgument()
|
||||
{
|
||||
if (!CheckParseState(ParseState.Start))
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
if (InstNesting == 0)
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
State = ParseState.Start;
|
||||
NestedName = false;
|
||||
if (!FirstInstArg)
|
||||
{
|
||||
TypeString.Append(',');
|
||||
}
|
||||
else
|
||||
{
|
||||
FirstInstArg = false;
|
||||
}
|
||||
|
||||
TypeString.Append(UseAngleBracketsForGenerics ? '<' : '[');
|
||||
PushOpenGenericArgument();
|
||||
}
|
||||
|
||||
private void CloseGenericArgument()
|
||||
{
|
||||
if (!CheckParseState(ParseState.Name | ParseState.GenArgs | ParseState.PtrArr | ParseState.ByRef | ParseState.AssemSpec))
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
if (InstNesting == 0)
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
State = ParseState.Start;
|
||||
|
||||
if (HasAssemblySpec)
|
||||
{
|
||||
TypeString.Append(UseAngleBracketsForGenerics ? '>' : ']');
|
||||
}
|
||||
|
||||
PopOpenGenericArgument();
|
||||
}
|
||||
|
||||
private void CloseGenericArguments()
|
||||
{
|
||||
if (InstNesting == 0)
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
if (!CheckParseState(ParseState.Start))
|
||||
{
|
||||
Fail();
|
||||
}
|
||||
|
||||
State = ParseState.GenArgs;
|
||||
InstNesting--;
|
||||
|
||||
if (FirstInstArg)
|
||||
{
|
||||
if (TypeString.Length > 0)
|
||||
TypeString.Remove(TypeString.Length - 1, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeString.Append(UseAngleBracketsForGenerics ? '>' : ']');
|
||||
}
|
||||
}
|
||||
private void PushOpenGenericArgument()
|
||||
{
|
||||
GenericStartsStack ??= new();
|
||||
GenericStartsStack.Push(TypeString.Length);
|
||||
}
|
||||
|
||||
private void PopOpenGenericArgument()
|
||||
{
|
||||
int strIndex = GenericStartsStack!.Pop();
|
||||
|
||||
if (!HasAssemblySpec)
|
||||
{
|
||||
TypeString.Remove(strIndex - 1, 1);
|
||||
}
|
||||
HasAssemblySpec = false;
|
||||
}
|
||||
|
||||
private void AddAssemblySpec(string? assemblySpec)
|
||||
{
|
||||
if (!CheckParseState(ParseState.Name | ParseState.GenArgs | ParseState.PtrArr | ParseState.ByRef))
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
State = ParseState.AssemSpec;
|
||||
if (!string.IsNullOrEmpty(assemblySpec))
|
||||
{
|
||||
TypeString.Append(", ");
|
||||
|
||||
if (InstNesting > 0)
|
||||
EscapeEmbeddedAssemblyName(assemblySpec);
|
||||
else
|
||||
EscapeAssemblyName(assemblySpec);
|
||||
|
||||
HasAssemblySpec = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendNestedTypeDef(ref TypeNameBuilder tnb, EcmaMetadataReader reader, uint typeDefToken, TypeNameFormat format)
|
||||
{
|
||||
EcmaMetadataCursor cursor = reader.GetCursor(typeDefToken);
|
||||
System.Reflection.TypeAttributes typeDefAttributes = (System.Reflection.TypeAttributes)reader.GetColumnAsConstant(cursor, MetadataColumnIndex.TypeDef_Flags);
|
||||
if ((int)(typeDefAttributes & System.Reflection.TypeAttributes.VisibilityMask) >= (int)System.Reflection.TypeAttributes.NestedPublic)
|
||||
{
|
||||
uint currentTypeDefToken = typeDefToken;
|
||||
List<uint> nestedTokens = new();
|
||||
EcmaMetadataCursor nestedTypesCursor = reader.GetCursor(EcmaMetadataReader.CreateToken(MetadataTable.NestedClass, 1));
|
||||
while (reader.TryFindRowFromCursor(nestedTypesCursor, MetadataColumnIndex.NestedClass_NestedClass, currentTypeDefToken, out EcmaMetadataCursor foundNestedClassRecord))
|
||||
{
|
||||
currentTypeDefToken = reader.GetColumnAsToken(foundNestedClassRecord, MetadataColumnIndex.NestedClass_EnclosingClass);
|
||||
nestedTokens.Add(currentTypeDefToken);
|
||||
}
|
||||
|
||||
for (int i = nestedTokens.Count - 1; i >= 0; i--)
|
||||
{
|
||||
AppendTypeDef(ref tnb, reader, nestedTokens[i], format);
|
||||
}
|
||||
}
|
||||
AppendTypeDef(ref tnb, reader, typeDefToken, format);
|
||||
}
|
||||
|
||||
private static void AppendTypeDef(ref TypeNameBuilder tnb, EcmaMetadataReader reader, uint typeDefToken, TypeNameFormat format)
|
||||
{
|
||||
EcmaMetadataCursor cursor = reader.GetCursor(typeDefToken);
|
||||
string? typeNamespace = null;
|
||||
if (format.HasFlag(TypeNameFormat.FormatNamespace))
|
||||
{
|
||||
typeNamespace = reader.GetColumnAsUtf8String(cursor, MetadataColumnIndex.TypeDef_TypeNamespace);
|
||||
}
|
||||
tnb.AddName(reader.GetColumnAsUtf8String(cursor, MetadataColumnIndex.TypeDef_TypeName), typeNamespace);
|
||||
}
|
||||
|
||||
private static void AppendParamTypeQualifier(ref TypeNameBuilder tnb, CorElementType kind, uint rank)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case CorElementType.Byref:
|
||||
tnb.AddByRef();
|
||||
break;
|
||||
case CorElementType.Ptr:
|
||||
tnb.AddPointer();
|
||||
break;
|
||||
case CorElementType.SzArray:
|
||||
tnb.AddSzArray();
|
||||
break;
|
||||
case CorElementType.Array:
|
||||
tnb.AddArray(rank);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddByRef()
|
||||
{
|
||||
if (!CheckParseState(ParseState.Name | ParseState.GenArgs | ParseState.PtrArr))
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
State = ParseState.ByRef;
|
||||
TypeString.Append('&');
|
||||
}
|
||||
|
||||
private void AddPointer()
|
||||
{
|
||||
if (!CheckParseState(ParseState.Name | ParseState.GenArgs | ParseState.PtrArr))
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
State = ParseState.PtrArr;
|
||||
TypeString.Append('*');
|
||||
}
|
||||
|
||||
private void AddSzArray()
|
||||
{
|
||||
if (!CheckParseState(ParseState.Name | ParseState.GenArgs | ParseState.PtrArr))
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
State = ParseState.PtrArr;
|
||||
TypeString.Append("[]");
|
||||
}
|
||||
|
||||
private void AddArray(uint rank)
|
||||
{
|
||||
if (!CheckParseState(ParseState.Name | ParseState.GenArgs | ParseState.PtrArr))
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
State = ParseState.PtrArr;
|
||||
if (rank == 0)
|
||||
return;
|
||||
|
||||
if (rank == 1)
|
||||
{
|
||||
TypeString.Append("[*]");
|
||||
}
|
||||
else if (rank > 64)
|
||||
{
|
||||
TypeString.Append($"[{rank}]");
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeString.Append('[');
|
||||
for (uint i = 1; i < rank; i++)
|
||||
{
|
||||
TypeString.Append(',');
|
||||
}
|
||||
TypeString.Append(']');
|
||||
}
|
||||
TypeString.Append("[]");
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<char> TypeNameReservedChars()
|
||||
{
|
||||
return ",[]&*+\\";
|
||||
}
|
||||
|
||||
private static bool IsTypeNameReservedChar(char c)
|
||||
{
|
||||
return TypeNameReservedChars().IndexOf(c) != -1;
|
||||
}
|
||||
|
||||
private void EscapeName(string name)
|
||||
{
|
||||
foreach (char c in name)
|
||||
{
|
||||
if (IsTypeNameReservedChar(c))
|
||||
{
|
||||
TypeString.Append('\\');
|
||||
}
|
||||
TypeString.Append(c);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddName(string name, string? _namespace = null)
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckParseState(ParseState.Start | ParseState.Name))
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
State = ParseState.Name;
|
||||
if (NestedName)
|
||||
TypeString.Append('+');
|
||||
|
||||
NestedName = true;
|
||||
if (!string.IsNullOrEmpty(_namespace))
|
||||
{
|
||||
EscapeName(_namespace);
|
||||
TypeString.Append('.');
|
||||
}
|
||||
|
||||
EscapeName(name);
|
||||
}
|
||||
|
||||
private void EscapeAssemblyName(string assemblyName)
|
||||
{
|
||||
TypeString.Append(assemblyName);
|
||||
}
|
||||
|
||||
private void EscapeEmbeddedAssemblyName(string assemblyName)
|
||||
{
|
||||
foreach (char c in assemblyName)
|
||||
{
|
||||
if (c == ']')
|
||||
TypeString.Append('\\');
|
||||
TypeString.Append(c);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddNameNoEscaping(StringBuilder? name)
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckParseState(ParseState.Start | ParseState.Name))
|
||||
{
|
||||
Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
State = ParseState.Name;
|
||||
|
||||
if (NestedName)
|
||||
TypeString.Append('+');
|
||||
|
||||
NestedName = true;
|
||||
TypeString.Append(name);
|
||||
}
|
||||
|
||||
private void Fail()
|
||||
{
|
||||
State = ParseState.Error;
|
||||
}
|
||||
|
||||
private bool CheckParseState(ParseState validStates)
|
||||
{
|
||||
// Error is always invalid
|
||||
if (State == ParseState.Error)
|
||||
return false;
|
||||
|
||||
return (State & validStates) != 0;
|
||||
}
|
||||
}
|
|
@ -36,6 +36,18 @@ public readonly struct TargetNUInt
|
|||
public TargetNUInt(ulong value) => Value = value;
|
||||
}
|
||||
|
||||
public readonly struct TargetSpan
|
||||
{
|
||||
public TargetSpan(TargetPointer address, ulong size)
|
||||
{
|
||||
Address = address;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
public TargetPointer Address { get; }
|
||||
public ulong Size { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Representation of the target under inspection
|
||||
/// </summary>
|
||||
|
@ -80,6 +92,7 @@ public sealed unsafe class Target
|
|||
|
||||
internal Contracts.Registry Contracts { get; }
|
||||
internal DataCache ProcessedData { get; }
|
||||
internal Helpers.Metadata Metadata { get; }
|
||||
|
||||
public static bool TryCreate(ulong contractDescriptor, delegate* unmanaged<ulong, byte*, uint, void*, int> readFromTarget, void* readContext, out Target? target)
|
||||
{
|
||||
|
@ -98,6 +111,7 @@ public sealed unsafe class Target
|
|||
{
|
||||
Contracts = new Contracts.Registry(this);
|
||||
ProcessedData = new DataCache(this);
|
||||
Metadata = new Helpers.Metadata(this);
|
||||
_config = config;
|
||||
_reader = reader;
|
||||
|
||||
|
@ -246,6 +260,8 @@ public sealed unsafe class Target
|
|||
return DataType.Unknown;
|
||||
}
|
||||
|
||||
public int PointerSize => _config.PointerSize;
|
||||
|
||||
public T Read<T>(ulong address) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
|
||||
{
|
||||
if (!TryRead(address, _config.IsLittleEndian, _reader, out T value))
|
||||
|
@ -266,6 +282,17 @@ public sealed unsafe class Target
|
|||
: T.TryReadBigEndian(buffer, !IsSigned<T>(), out value);
|
||||
}
|
||||
|
||||
public void ReadBuffer(ulong address, Span<byte> buffer)
|
||||
{
|
||||
if (!TryReadBuffer(address, buffer))
|
||||
throw new InvalidOperationException($"Failed to read {buffer.Length} bytes at 0x{address:x8}.");
|
||||
}
|
||||
|
||||
private bool TryReadBuffer(ulong address, Span<byte> buffer)
|
||||
{
|
||||
return _reader.ReadFromTarget(address, buffer) >= 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsSigned<T>() where T : struct, INumberBase<T>, IMinMaxValue<T>
|
||||
{
|
||||
|
@ -280,6 +307,19 @@ public sealed unsafe class Target
|
|||
return pointer;
|
||||
}
|
||||
|
||||
public void ReadPointers(ulong address, Span<TargetPointer> buffer)
|
||||
{
|
||||
// TODO(cdac) - This could do a single read, and then swizzle in place if it is useful for performance
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
buffer[i] = ReadPointer(address);
|
||||
checked
|
||||
{
|
||||
address += (ulong)_config.PointerSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TargetNUInt ReadNUInt(ulong address)
|
||||
{
|
||||
if (!TryReadNUInt(address, _config, _reader, out ulong value))
|
||||
|
|
244
src/native/managed/cdacreader/tests/DacStreamsTests.cs
Normal file
244
src/native/managed/cdacreader/tests/DacStreamsTests.cs
Normal file
|
@ -0,0 +1,244 @@
|
|||
// 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.Text;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Diagnostics.DataContractReader.UnitTests;
|
||||
|
||||
public class DacStreamsTests
|
||||
{
|
||||
private delegate MockMemorySpace.Builder ConfigureContextBuilder(MockMemorySpace.Builder builder);
|
||||
|
||||
const ulong TestMiniMetaDataBuffGlobalAddress = 0x00000000_200000a0;
|
||||
const ulong TestMiniMetaDataBuffGlobalMaxSize = 0x00000000_20000000;
|
||||
const ulong TestMiniMetaDataBuffAddress = 0x00000000_100000a0;
|
||||
|
||||
const uint MiniMetadataSignature = 0x6d727473;
|
||||
const uint EENameStreamSignature = 0x614e4545;
|
||||
|
||||
const uint MiniMetaDataStreamsHeaderSize = 12;
|
||||
|
||||
private static readonly (DataType Type, Target.TypeInfo Info)[] DacStreamsTypes =
|
||||
[
|
||||
];
|
||||
|
||||
private static readonly (string Name, ulong Value, string? Type)[] DacStreamsGlobals =
|
||||
[
|
||||
(nameof(Constants.Globals.MiniMetaDataBuffAddress), TestMiniMetaDataBuffGlobalAddress, null),
|
||||
(nameof(Constants.Globals.MiniMetaDataBuffMaxSize), TestMiniMetaDataBuffGlobalMaxSize, null),
|
||||
];
|
||||
|
||||
private static unsafe void DacStreamsContractHelper(MockTarget.Architecture arch, ConfigureContextBuilder configure, Action<Target> testCase)
|
||||
{
|
||||
TargetTestHelpers targetTestHelpers = new(arch);
|
||||
string metadataTypesJson = TargetTestHelpers.MakeTypesJson(DacStreamsTypes);
|
||||
string metadataGlobalsJson = TargetTestHelpers.MakeGlobalsJson(DacStreamsGlobals);
|
||||
byte[] json = Encoding.UTF8.GetBytes($$"""
|
||||
{
|
||||
"version": 0,
|
||||
"baseline": "empty",
|
||||
"contracts": {
|
||||
"{{nameof(Contracts.DacStreams)}}": 1
|
||||
},
|
||||
"types": { {{metadataTypesJson}} },
|
||||
"globals": { {{metadataGlobalsJson}} }
|
||||
}
|
||||
""");
|
||||
Span<byte> descriptor = stackalloc byte[targetTestHelpers.ContractDescriptorSize];
|
||||
targetTestHelpers.ContractDescriptorFill(descriptor, json.Length, DacStreamsGlobals.Length);
|
||||
|
||||
int pointerSize = targetTestHelpers.PointerSize;
|
||||
Span<byte> pointerData = stackalloc byte[DacStreamsGlobals.Length * pointerSize];
|
||||
for (int i = 0; i < DacStreamsGlobals.Length; i++)
|
||||
{
|
||||
var (_, value, _) = DacStreamsGlobals[i];
|
||||
targetTestHelpers.WritePointer(pointerData.Slice(i * pointerSize), value);
|
||||
}
|
||||
|
||||
fixed (byte* jsonPtr = json)
|
||||
{
|
||||
MockMemorySpace.Builder builder = new();
|
||||
|
||||
builder = builder.SetDescriptor(descriptor)
|
||||
.SetJson(json)
|
||||
.SetPointerData(pointerData);
|
||||
|
||||
if (configure != null)
|
||||
{
|
||||
builder = configure(builder);
|
||||
}
|
||||
|
||||
using MockMemorySpace.ReadContext context = builder.Create();
|
||||
|
||||
bool success = MockMemorySpace.TryCreateTarget(&context, out Target? target);
|
||||
Assert.True(success);
|
||||
|
||||
testCase(target);
|
||||
}
|
||||
GC.KeepAlive(json);
|
||||
}
|
||||
|
||||
MockMemorySpace.Builder AddMiniMetaDataBuffMaxSize(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, uint maxSize)
|
||||
{
|
||||
MockMemorySpace.HeapFragment globalAddr = new() { Name = "Address of MiniMetaDataBuffMaxSize", Address = TestMiniMetaDataBuffGlobalMaxSize, Data = new byte[4] };
|
||||
targetTestHelpers.Write(globalAddr.Data, maxSize);
|
||||
return builder.AddHeapFragment(globalAddr);
|
||||
}
|
||||
|
||||
MockMemorySpace.Builder AddMiniMetaDataBuffAddress(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, ulong pointer)
|
||||
{
|
||||
MockMemorySpace.HeapFragment globalAddr = new() { Name = "Address of MiniMetaDataBuffAddress", Address = TestMiniMetaDataBuffGlobalAddress, Data = new byte[targetTestHelpers.PointerSize] };
|
||||
targetTestHelpers.WritePointer(globalAddr.Data, pointer);
|
||||
return builder.AddHeapFragment(globalAddr);
|
||||
}
|
||||
|
||||
private class CurrentPointer
|
||||
{
|
||||
public ulong Pointer;
|
||||
}
|
||||
|
||||
MockMemorySpace.Builder AddMiniMetaDataStreamsHeader(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, uint totalSizeOtherThanStreamsHeader, uint countStreams, CurrentPointer currentPointer)
|
||||
{
|
||||
MockMemorySpace.HeapFragment globalAddr = new() { Name = "MiniMetaDataStreamsHeader", Address = currentPointer.Pointer, Data = new byte[MiniMetaDataStreamsHeaderSize] };
|
||||
targetTestHelpers.Write(globalAddr.Data.AsSpan().Slice(0, 4), MiniMetadataSignature);
|
||||
targetTestHelpers.Write(globalAddr.Data.AsSpan().Slice(4, 4), totalSizeOtherThanStreamsHeader + MiniMetaDataStreamsHeaderSize);
|
||||
targetTestHelpers.Write(globalAddr.Data.AsSpan().Slice(8, 4), countStreams);
|
||||
currentPointer.Pointer += 12;
|
||||
return builder.AddHeapFragment(globalAddr);
|
||||
}
|
||||
|
||||
MockMemorySpace.Builder AddEENameStreamHeader(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, uint countEntries, CurrentPointer currentPointer)
|
||||
{
|
||||
MockMemorySpace.HeapFragment globalAddr = new() { Name = "EEStreamHeader", Address = currentPointer.Pointer, Data = new byte[8] };
|
||||
targetTestHelpers.Write(globalAddr.Data.AsSpan().Slice(0, 4), EENameStreamSignature);
|
||||
targetTestHelpers.Write(globalAddr.Data.AsSpan().Slice(4, 4), countEntries);
|
||||
currentPointer.Pointer += 8;
|
||||
return builder.AddHeapFragment(globalAddr);
|
||||
}
|
||||
|
||||
|
||||
MockMemorySpace.Builder AddEENameStream(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, List<(ulong Pointer, string Name)> names, CurrentPointer currentPointer)
|
||||
{
|
||||
builder = AddEENameStreamHeader(targetTestHelpers, builder, checked((uint)names.Count), currentPointer);
|
||||
|
||||
for (int i = 0; i < names.Count; i++)
|
||||
{
|
||||
int byteCountWithoutNullTerminator = Encoding.UTF8.GetByteCount(names[i].Name);
|
||||
int entrySize = byteCountWithoutNullTerminator + 1 + targetTestHelpers.PointerSize;
|
||||
MockMemorySpace.HeapFragment entryAddr = new()
|
||||
{
|
||||
Name = $"EEStreamEntry{i}", Address = currentPointer.Pointer, Data = new byte[byteCountWithoutNullTerminator + 1 + targetTestHelpers.PointerSize]
|
||||
};
|
||||
targetTestHelpers.WritePointer(entryAddr.Data.AsSpan().Slice(0, targetTestHelpers.PointerSize), names[i].Pointer);
|
||||
Encoding.UTF8.TryGetBytes(names[i].Name.AsSpan(), entryAddr.Data.AsSpan().Slice(targetTestHelpers.PointerSize, byteCountWithoutNullTerminator), out _);
|
||||
targetTestHelpers.Write(entryAddr.Data.AsSpan().Slice(byteCountWithoutNullTerminator + targetTestHelpers.PointerSize, 1), (byte)0);
|
||||
currentPointer.Pointer += (ulong)entrySize;
|
||||
builder = builder.AddHeapFragment(entryAddr);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ClassData(typeof(MockTarget.StdArch))]
|
||||
public void DacStreamValues(MockTarget.Architecture arch)
|
||||
{
|
||||
TargetTestHelpers targetTestHelpers = new(arch);
|
||||
|
||||
DacStreamsContractHelper(arch,
|
||||
(builder) =>
|
||||
{
|
||||
// Test normal non-error behavior
|
||||
|
||||
List<(ulong Pointer, string Name)> values = [((ulong)0x1234, "Type1"), ((ulong)0x1238, "Type2")];
|
||||
builder = AddMiniMetaDataBuffAddress(targetTestHelpers, builder, TestMiniMetaDataBuffAddress);
|
||||
builder = AddMiniMetaDataBuffMaxSize(targetTestHelpers, builder, 0x10000);
|
||||
CurrentPointer currentPointer = new();
|
||||
ulong eeNameStreamStart = TestMiniMetaDataBuffAddress + MiniMetaDataStreamsHeaderSize;
|
||||
currentPointer.Pointer = TestMiniMetaDataBuffAddress + MiniMetaDataStreamsHeaderSize;
|
||||
builder = AddEENameStream(targetTestHelpers, builder, values, currentPointer);
|
||||
uint eeNameStreamSize = checked((uint)(currentPointer.Pointer - eeNameStreamStart));
|
||||
|
||||
currentPointer.Pointer = TestMiniMetaDataBuffAddress;
|
||||
builder = AddMiniMetaDataStreamsHeader(targetTestHelpers, builder, eeNameStreamSize, 1, currentPointer);
|
||||
return builder;
|
||||
},
|
||||
(target) =>
|
||||
{
|
||||
Contracts.IDacStreams dacStreamsContract = target.Contracts.DacStreams;
|
||||
Assert.NotNull(dacStreamsContract);
|
||||
Assert.Null(dacStreamsContract.StringFromEEAddress(0));
|
||||
Assert.Equal("Type1", dacStreamsContract.StringFromEEAddress(0x1234));
|
||||
Assert.Equal("Type2", dacStreamsContract.StringFromEEAddress(0x1238));
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ClassData(typeof(MockTarget.StdArch))]
|
||||
public void DacStreamValues_TruncatedTotalSize(MockTarget.Architecture arch)
|
||||
{
|
||||
// Test behavior if TotalSize isn't big enough to hold the last entry
|
||||
|
||||
TargetTestHelpers targetTestHelpers = new(arch);
|
||||
|
||||
DacStreamsContractHelper(arch,
|
||||
(builder) =>
|
||||
{
|
||||
List<(ulong Pointer, string Name)> values = [((ulong)0x1234, "Type1"), ((ulong)0x1238, "Type2")];
|
||||
builder = AddMiniMetaDataBuffAddress(targetTestHelpers, builder, TestMiniMetaDataBuffAddress);
|
||||
builder = AddMiniMetaDataBuffMaxSize(targetTestHelpers, builder, 0x10000);
|
||||
CurrentPointer currentPointer = new();
|
||||
ulong eeNameStreamStart = TestMiniMetaDataBuffAddress + MiniMetaDataStreamsHeaderSize;
|
||||
currentPointer.Pointer = TestMiniMetaDataBuffAddress + MiniMetaDataStreamsHeaderSize;
|
||||
builder = AddEENameStream(targetTestHelpers, builder, values, currentPointer);
|
||||
uint eeNameStreamSize = checked((uint)(currentPointer.Pointer - eeNameStreamStart));
|
||||
|
||||
currentPointer.Pointer = TestMiniMetaDataBuffAddress;
|
||||
builder = AddMiniMetaDataStreamsHeader(targetTestHelpers, builder, eeNameStreamSize - 2, 1, currentPointer);
|
||||
return builder;
|
||||
},
|
||||
(target) =>
|
||||
{
|
||||
Contracts.IDacStreams dacStreamsContract = target.Contracts.DacStreams;
|
||||
Assert.NotNull(dacStreamsContract);
|
||||
Assert.Null(dacStreamsContract.StringFromEEAddress(0));
|
||||
Assert.Equal("Type1", dacStreamsContract.StringFromEEAddress(0x1234));
|
||||
Assert.Null(dacStreamsContract.StringFromEEAddress(0x1238));
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[ClassData(typeof(MockTarget.StdArch))]
|
||||
public void DacStreamValues_TruncatedBuffMaxSize(MockTarget.Architecture arch)
|
||||
{
|
||||
// Test behavior if MaxSize global is smaller than TotalSize
|
||||
TargetTestHelpers targetTestHelpers = new(arch);
|
||||
|
||||
DacStreamsContractHelper(arch,
|
||||
(builder) =>
|
||||
{
|
||||
List<(ulong Pointer, string Name)> values = [((ulong)0x1234, "Type1"), ((ulong)0x1238, "Type2")];
|
||||
builder = AddMiniMetaDataBuffAddress(targetTestHelpers, builder, TestMiniMetaDataBuffAddress);
|
||||
builder = AddMiniMetaDataBuffMaxSize(targetTestHelpers, builder, (uint)(0x20 + targetTestHelpers.PointerSize * 2 - 1));
|
||||
CurrentPointer currentPointer = new();
|
||||
ulong eeNameStreamStart = TestMiniMetaDataBuffAddress + MiniMetaDataStreamsHeaderSize;
|
||||
currentPointer.Pointer = TestMiniMetaDataBuffAddress + MiniMetaDataStreamsHeaderSize;
|
||||
builder = AddEENameStream(targetTestHelpers, builder, values, currentPointer);
|
||||
uint eeNameStreamSize = checked((uint)(currentPointer.Pointer - eeNameStreamStart));
|
||||
|
||||
currentPointer.Pointer = TestMiniMetaDataBuffAddress;
|
||||
builder = AddMiniMetaDataStreamsHeader(targetTestHelpers, builder, eeNameStreamSize, 1, currentPointer);
|
||||
return builder;
|
||||
},
|
||||
(target) =>
|
||||
{
|
||||
Contracts.IDacStreams dacStreamsContract = target.Contracts.DacStreams;
|
||||
Assert.NotNull(dacStreamsContract);
|
||||
Assert.Null(dacStreamsContract.StringFromEEAddress(0));
|
||||
Assert.Null(dacStreamsContract.StringFromEEAddress(0x1234));
|
||||
Assert.Null(dacStreamsContract.StringFromEEAddress(0x1238));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ public unsafe class MethodTableTests
|
|||
{ nameof(Data.MethodTable.ParentMethodTable), new () { Offset = 40, Type = DataType.pointer}},
|
||||
{ nameof(Data.MethodTable.NumInterfaces), new () { Offset = 48, Type = DataType.uint16}},
|
||||
{ nameof(Data.MethodTable.NumVirtuals), new () { Offset = 50, Type = DataType.uint16}},
|
||||
{ nameof(Data.MethodTable.PerInstInfo), new () { Offset = 56, Type = DataType.pointer}},
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -33,6 +34,7 @@ public unsafe class MethodTableTests
|
|||
{ nameof (Data.EEClass.MethodTable), new () { Offset = 8, Type = DataType.pointer}},
|
||||
{ nameof (Data.EEClass.CorTypeAttr), new () { Offset = 16, Type = DataType.uint32}},
|
||||
{ nameof (Data.EEClass.NumMethods), new () { Offset = 20, Type = DataType.uint16}},
|
||||
{ nameof (Data.EEClass.InternalCorElementType), new () { Offset = 22, Type = DataType.uint8}},
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -150,7 +152,7 @@ public unsafe class MethodTableTests
|
|||
{
|
||||
Contracts.IRuntimeTypeSystem metadataContract = target.Contracts.RuntimeTypeSystem;
|
||||
Assert.NotNull(metadataContract);
|
||||
Contracts.MethodTableHandle handle = metadataContract.GetMethodTableHandle(TestFreeObjectMethodTableAddress);
|
||||
Contracts.TypeHandle handle = metadataContract.GetTypeHandle(TestFreeObjectMethodTableAddress);
|
||||
Assert.NotEqual(TargetPointer.Null, handle.Address);
|
||||
Assert.True(metadataContract.IsFreeObjectMethodTable(handle));
|
||||
});
|
||||
|
@ -187,9 +189,9 @@ public unsafe class MethodTableTests
|
|||
{
|
||||
Contracts.IRuntimeTypeSystem metadataContract = target.Contracts.RuntimeTypeSystem;
|
||||
Assert.NotNull(metadataContract);
|
||||
Contracts.MethodTableHandle systemObjectMethodTableHandle = metadataContract.GetMethodTableHandle(systemObjectMethodTablePtr);
|
||||
Assert.Equal(systemObjectMethodTablePtr.Value, systemObjectMethodTableHandle.Address.Value);
|
||||
Assert.False(metadataContract.IsFreeObjectMethodTable(systemObjectMethodTableHandle));
|
||||
Contracts.TypeHandle systemObjectTypeHandle = metadataContract.GetTypeHandle(systemObjectMethodTablePtr);
|
||||
Assert.Equal(systemObjectMethodTablePtr.Value, systemObjectTypeHandle.Address.Value);
|
||||
Assert.False(metadataContract.IsFreeObjectMethodTable(systemObjectTypeHandle));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -226,10 +228,10 @@ public unsafe class MethodTableTests
|
|||
{
|
||||
Contracts.IRuntimeTypeSystem metadataContract = target.Contracts.RuntimeTypeSystem;
|
||||
Assert.NotNull(metadataContract);
|
||||
Contracts.MethodTableHandle systemStringMethodTableHandle = metadataContract.GetMethodTableHandle(systemStringMethodTablePtr);
|
||||
Assert.Equal(systemStringMethodTablePtr.Value, systemStringMethodTableHandle.Address.Value);
|
||||
Assert.False(metadataContract.IsFreeObjectMethodTable(systemStringMethodTableHandle));
|
||||
Assert.True(metadataContract.IsString(systemStringMethodTableHandle));
|
||||
Contracts.TypeHandle systemStringTypeHandle = metadataContract.GetTypeHandle(systemStringMethodTablePtr);
|
||||
Assert.Equal(systemStringMethodTablePtr.Value, systemStringTypeHandle.Address.Value);
|
||||
Assert.False(metadataContract.IsFreeObjectMethodTable(systemStringTypeHandle));
|
||||
Assert.True(metadataContract.IsString(systemStringTypeHandle));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -258,7 +260,7 @@ public unsafe class MethodTableTests
|
|||
{
|
||||
Contracts.IRuntimeTypeSystem metadataContract = target.Contracts.RuntimeTypeSystem;
|
||||
Assert.NotNull(metadataContract);
|
||||
Assert.Throws<InvalidOperationException>(() => metadataContract.GetMethodTableHandle(badMethodTablePtr));
|
||||
Assert.Throws<InvalidOperationException>(() => metadataContract.GetTypeHandle(badMethodTablePtr));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -308,11 +310,11 @@ public unsafe class MethodTableTests
|
|||
{
|
||||
Contracts.IRuntimeTypeSystem metadataContract = target.Contracts.RuntimeTypeSystem;
|
||||
Assert.NotNull(metadataContract);
|
||||
Contracts.MethodTableHandle genericInstanceMethodTableHandle = metadataContract.GetMethodTableHandle(genericInstanceMethodTablePtr);
|
||||
Assert.Equal(genericInstanceMethodTablePtr.Value, genericInstanceMethodTableHandle.Address.Value);
|
||||
Assert.False(metadataContract.IsFreeObjectMethodTable(genericInstanceMethodTableHandle));
|
||||
Assert.False(metadataContract.IsString(genericInstanceMethodTableHandle));
|
||||
Assert.Equal(numMethods, metadataContract.GetNumMethods(genericInstanceMethodTableHandle));
|
||||
Contracts.TypeHandle genericInstanceTypeHandle = metadataContract.GetTypeHandle(genericInstanceMethodTablePtr);
|
||||
Assert.Equal(genericInstanceMethodTablePtr.Value, genericInstanceTypeHandle.Address.Value);
|
||||
Assert.False(metadataContract.IsFreeObjectMethodTable(genericInstanceTypeHandle));
|
||||
Assert.False(metadataContract.IsString(genericInstanceTypeHandle));
|
||||
Assert.Equal(numMethods, metadataContract.GetNumMethods(genericInstanceTypeHandle));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -365,11 +367,11 @@ public unsafe class MethodTableTests
|
|||
{
|
||||
Contracts.IRuntimeTypeSystem metadataContract = target.Contracts.RuntimeTypeSystem;
|
||||
Assert.NotNull(metadataContract);
|
||||
Contracts.MethodTableHandle arrayInstanceMethodTableHandle = metadataContract.GetMethodTableHandle(arrayInstanceMethodTablePtr);
|
||||
Assert.Equal(arrayInstanceMethodTablePtr.Value, arrayInstanceMethodTableHandle.Address.Value);
|
||||
Assert.False(metadataContract.IsFreeObjectMethodTable(arrayInstanceMethodTableHandle));
|
||||
Assert.False(metadataContract.IsString(arrayInstanceMethodTableHandle));
|
||||
Assert.Equal(arrayInstanceComponentSize, metadataContract.GetComponentSize(arrayInstanceMethodTableHandle));
|
||||
Contracts.TypeHandle arrayInstanceTypeHandle = metadataContract.GetTypeHandle(arrayInstanceMethodTablePtr);
|
||||
Assert.Equal(arrayInstanceMethodTablePtr.Value, arrayInstanceTypeHandle.Address.Value);
|
||||
Assert.False(metadataContract.IsFreeObjectMethodTable(arrayInstanceTypeHandle));
|
||||
Assert.False(metadataContract.IsString(arrayInstanceTypeHandle));
|
||||
Assert.Equal(arrayInstanceComponentSize, metadataContract.GetComponentSize(arrayInstanceTypeHandle));
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
@ -234,23 +234,41 @@ internal unsafe static class MockMemorySpace
|
|||
|
||||
public int ReadFragment(ulong address, Span<byte> buffer)
|
||||
{
|
||||
foreach (var fragment in _fragments)
|
||||
bool partialReadOcurred = false;
|
||||
HeapFragment lastHeapFragment = default;
|
||||
int availableLength = 0;
|
||||
while (true)
|
||||
{
|
||||
if (address >= fragment.Address && address < fragment.Address + (ulong)fragment.Data.Length)
|
||||
bool tryAgain = false;
|
||||
foreach (var fragment in _fragments)
|
||||
{
|
||||
int offset = (int)(address - fragment.Address);
|
||||
int availableLength = fragment.Data.Length - offset;
|
||||
if (availableLength >= buffer.Length)
|
||||
if (address >= fragment.Address && address < fragment.Address + (ulong)fragment.Data.Length)
|
||||
{
|
||||
fragment.Data.AsSpan(offset, buffer.Length).CopyTo(buffer);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Not enough data in fragment at {fragment.Address:X} ('{fragment.Name}') to read {buffer.Length} bytes at {address:X} (only {availableLength} bytes available)");
|
||||
int offset = (int)(address - fragment.Address);
|
||||
availableLength = fragment.Data.Length - offset;
|
||||
if (availableLength >= buffer.Length)
|
||||
{
|
||||
fragment.Data.AsSpan(offset, buffer.Length).CopyTo(buffer);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastHeapFragment = fragment;
|
||||
partialReadOcurred = true;
|
||||
tryAgain = true;
|
||||
fragment.Data.AsSpan(offset, availableLength).CopyTo(buffer);
|
||||
buffer = buffer.Slice(availableLength);
|
||||
address = fragment.Address + (ulong)fragment.Data.Length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!tryAgain)
|
||||
break;
|
||||
}
|
||||
|
||||
if (partialReadOcurred)
|
||||
throw new InvalidOperationException($"Not enough data in fragment at {lastHeapFragment.Address:X} ('{lastHeapFragment.Name}') to read {buffer.Length} bytes at {address:X} (only {availableLength} bytes available)");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue