Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts;
public interface ISignatureDecoder : IContract
{
static string IContract.Name { get; } = nameof(SignatureDecoder);

TypeHandle DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle moduleHandle, TypeHandle ctx) => throw new NotImplementedException();
}
Comment thread
max-charlamb marked this conversation as resolved.
Outdated

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Reflection.Metadata;

namespace Microsoft.Diagnostics.DataContractReader.SignatureHelpers;

/// <summary>
/// Superset of SRM's <see cref="ISignatureTypeProvider{TType, TGenericContext}"/>
/// that adds support for runtime-internal type codes
/// (<c>ELEMENT_TYPE_INTERNAL</c> 0x21 and <c>ELEMENT_TYPE_CMOD_INTERNAL</c> 0x22).
/// </summary>
/// <remarks>
/// Providers implementing this interface automatically satisfy SRM's
/// <see cref="ISignatureTypeProvider{TType, TGenericContext}"/> and can be used
/// with both SRM's <c>SignatureDecoder</c> and our
/// <see cref="RuntimeSignatureDecoder{TType, TGenericContext}"/>.
/// </remarks>
internal interface IRuntimeSignatureTypeProvider<TType, TGenericContext>
: ISignatureTypeProvider<TType, TGenericContext>
{
/// <summary>
/// Classify an <c>ELEMENT_TYPE_INTERNAL</c> (0x21) type by resolving the
/// embedded TypeHandle pointer via the target's runtime type system.
/// </summary>
TType GetInternalType(Target target, TargetPointer typeHandlePointer);

/// <summary>
/// Classify an <c>ELEMENT_TYPE_CMOD_INTERNAL</c> (0x22) custom modifier by
/// resolving the embedded TypeHandle pointer via the target's runtime type system.
/// </summary>
TType GetInternalModifiedType(Target target, TargetPointer typeHandlePointer, TType unmodifiedType, bool isRequired);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Immutable;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;

namespace Microsoft.Diagnostics.DataContractReader.SignatureHelpers;

/// <summary>
/// Decodes signature blobs. Behaves identically to SRM's
/// <see cref="SignatureDecoder{TType, TGenericContext}"/> for standard ECMA-335 type codes,
/// with added support for runtime-internal types
/// (<c>ELEMENT_TYPE_INTERNAL</c> 0x21 and <c>ELEMENT_TYPE_CMOD_INTERNAL</c> 0x22).
/// </summary>
internal readonly struct RuntimeSignatureDecoder<TType, TGenericContext>
{
private const int ELEMENT_TYPE_CMOD_INTERNAL = 0x22;
private const int ELEMENT_TYPE_INTERNAL = 0x21;

private readonly IRuntimeSignatureTypeProvider<TType, TGenericContext> _provider;
private readonly MetadataReader _metadataReader;
private readonly Target _target;
private readonly TGenericContext _genericContext;
private readonly int _pointerSize;
Comment thread
max-charlamb marked this conversation as resolved.

public RuntimeSignatureDecoder(
IRuntimeSignatureTypeProvider<TType, TGenericContext> provider,
Target target,
MetadataReader metadataReader,
TGenericContext genericContext)
{
ArgumentNullException.ThrowIfNull(provider);
ArgumentNullException.ThrowIfNull(metadataReader);
Comment thread
max-charlamb marked this conversation as resolved.
Outdated

_provider = provider;
_metadataReader = metadataReader;
_target = target;
_genericContext = genericContext;
_pointerSize = target.PointerSize;
Comment thread
max-charlamb marked this conversation as resolved.
Comment thread
max-charlamb marked this conversation as resolved.
}
Comment thread
max-charlamb marked this conversation as resolved.

/// <summary>
/// Decodes a type embedded in a signature and advances the reader past the type.
/// </summary>
public TType DecodeType(ref BlobReader blobReader, bool allowTypeSpecifications = false)
{
return DecodeType(ref blobReader, allowTypeSpecifications, blobReader.ReadCompressedInteger());
}

private TType DecodeType(ref BlobReader blobReader, bool allowTypeSpecifications, int typeCode)
{
TType elementType;
int index;

switch (typeCode)
{
case (int)SignatureTypeCode.Boolean:
case (int)SignatureTypeCode.Char:
case (int)SignatureTypeCode.SByte:
case (int)SignatureTypeCode.Byte:
case (int)SignatureTypeCode.Int16:
case (int)SignatureTypeCode.UInt16:
case (int)SignatureTypeCode.Int32:
case (int)SignatureTypeCode.UInt32:
case (int)SignatureTypeCode.Int64:
case (int)SignatureTypeCode.UInt64:
case (int)SignatureTypeCode.Single:
case (int)SignatureTypeCode.Double:
case (int)SignatureTypeCode.IntPtr:
case (int)SignatureTypeCode.UIntPtr:
case (int)SignatureTypeCode.Object:
case (int)SignatureTypeCode.String:
case (int)SignatureTypeCode.Void:
case (int)SignatureTypeCode.TypedReference:
return _provider.GetPrimitiveType((PrimitiveTypeCode)typeCode);

case (int)SignatureTypeCode.Pointer:
elementType = DecodeType(ref blobReader);
return _provider.GetPointerType(elementType);

case (int)SignatureTypeCode.ByReference:
elementType = DecodeType(ref blobReader);
return _provider.GetByReferenceType(elementType);

case (int)SignatureTypeCode.Pinned:
elementType = DecodeType(ref blobReader);
return _provider.GetPinnedType(elementType);

case (int)SignatureTypeCode.SZArray:
elementType = DecodeType(ref blobReader);
return _provider.GetSZArrayType(elementType);

case (int)SignatureTypeCode.FunctionPointer:
MethodSignature<TType> methodSignature = DecodeMethodSignature(ref blobReader);
return _provider.GetFunctionPointerType(methodSignature);

case (int)SignatureTypeCode.Array:
return DecodeArrayType(ref blobReader);

case (int)SignatureTypeCode.RequiredModifier:
return DecodeModifiedType(ref blobReader, isRequired: true);

case (int)SignatureTypeCode.OptionalModifier:
return DecodeModifiedType(ref blobReader, isRequired: false);

case (int)SignatureTypeCode.GenericTypeInstance:
return DecodeGenericTypeInstance(ref blobReader);

case (int)SignatureTypeCode.GenericTypeParameter:
index = blobReader.ReadCompressedInteger();
return _provider.GetGenericTypeParameter(_genericContext, index);

case (int)SignatureTypeCode.GenericMethodParameter:
index = blobReader.ReadCompressedInteger();
return _provider.GetGenericMethodParameter(_genericContext, index);

case (int)SignatureTypeKind.Class:
case (int)SignatureTypeKind.ValueType:
return DecodeTypeHandle(ref blobReader, (byte)typeCode, allowTypeSpecifications);

case ELEMENT_TYPE_INTERNAL:
return DecodeInternalType(ref blobReader);

case ELEMENT_TYPE_CMOD_INTERNAL:
return DecodeInternalModifiedType(ref blobReader);

default:
throw new BadImageFormatException($"Unexpected signature type code: 0x{typeCode:X2}");
}
}

/// <summary>
/// Decodes a list of types, with at least one instance that is preceded by its count as a compressed integer.
/// </summary>
private ImmutableArray<TType> DecodeTypeSequence(ref BlobReader blobReader)
{
int count = blobReader.ReadCompressedInteger();
if (count == 0)
{
throw new BadImageFormatException("Signature type sequence must have at least one element");
Comment thread
max-charlamb marked this conversation as resolved.
}

var types = ImmutableArray.CreateBuilder<TType>(count);
for (int i = 0; i < count; i++)
{
types.Add(DecodeType(ref blobReader));
}
return types.MoveToImmutable();
}

/// <summary>
/// Decodes a method (definition, reference, or standalone) or property signature blob.
/// </summary>
public MethodSignature<TType> DecodeMethodSignature(ref BlobReader blobReader)
{
SignatureHeader header = blobReader.ReadSignatureHeader();
CheckMethodOrPropertyHeader(header);

int genericParameterCount = 0;
if (header.IsGeneric)
{
genericParameterCount = blobReader.ReadCompressedInteger();
}

int parameterCount = blobReader.ReadCompressedInteger();
TType returnType = DecodeType(ref blobReader);
ImmutableArray<TType> parameterTypes;
int requiredParameterCount;

if (parameterCount == 0)
{
requiredParameterCount = 0;
parameterTypes = ImmutableArray<TType>.Empty;
}
else
{
var parameterBuilder = ImmutableArray.CreateBuilder<TType>(parameterCount);
int parameterIndex;

for (parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++)
{
int typeCode = blobReader.ReadCompressedInteger();
if (typeCode == (int)SignatureTypeCode.Sentinel)
{
break;
}
parameterBuilder.Add(DecodeType(ref blobReader, allowTypeSpecifications: false, typeCode: typeCode));
}

requiredParameterCount = parameterIndex;
for (; parameterIndex < parameterCount; parameterIndex++)
{
parameterBuilder.Add(DecodeType(ref blobReader));
}
parameterTypes = parameterBuilder.MoveToImmutable();
}

return new MethodSignature<TType>(header, returnType, requiredParameterCount, genericParameterCount, parameterTypes);
}

/// <summary>
/// Decodes a local variable signature blob and advances the reader past the signature.
/// </summary>
public ImmutableArray<TType> DecodeLocalSignature(ref BlobReader blobReader)
{
SignatureHeader header = blobReader.ReadSignatureHeader();
CheckHeader(header, SignatureKind.LocalVariables);
return DecodeTypeSequence(ref blobReader);
}

/// <summary>
/// Decodes a field signature blob and advances the reader past the signature.
/// </summary>
public TType DecodeFieldSignature(ref BlobReader blobReader)
{
SignatureHeader header = blobReader.ReadSignatureHeader();
CheckHeader(header, SignatureKind.Field);
return DecodeType(ref blobReader);
}

private TType DecodeArrayType(ref BlobReader blobReader)
{
TType elementType = DecodeType(ref blobReader);
int rank = blobReader.ReadCompressedInteger();
var sizes = ImmutableArray<int>.Empty;
var lowerBounds = ImmutableArray<int>.Empty;

int sizesCount = blobReader.ReadCompressedInteger();
if (sizesCount > 0)
{
var builder = ImmutableArray.CreateBuilder<int>(sizesCount);
for (int i = 0; i < sizesCount; i++)
{
builder.Add(blobReader.ReadCompressedInteger());
}
sizes = builder.MoveToImmutable();
}

int lowerBoundsCount = blobReader.ReadCompressedInteger();
if (lowerBoundsCount > 0)
{
var builder = ImmutableArray.CreateBuilder<int>(lowerBoundsCount);
for (int i = 0; i < lowerBoundsCount; i++)
{
builder.Add(blobReader.ReadCompressedSignedInteger());
}
lowerBounds = builder.MoveToImmutable();
}

return _provider.GetArrayType(elementType, new ArrayShape(rank, sizes, lowerBounds));
}

private TType DecodeGenericTypeInstance(ref BlobReader blobReader)
{
TType genericType = DecodeType(ref blobReader);
ImmutableArray<TType> types = DecodeTypeSequence(ref blobReader);
return _provider.GetGenericInstantiation(genericType, types);
}

private TType DecodeModifiedType(ref BlobReader blobReader, bool isRequired)
{
// A standard modifier may be followed by an internal modifier; allow type specifications
// for the modifier handle (matches SRM behavior).
TType modifier = DecodeTypeHandle(ref blobReader, 0, allowTypeSpecifications: true);
TType unmodifiedType = DecodeType(ref blobReader);
return _provider.GetModifiedType(modifier, unmodifiedType, isRequired);
}

private TType DecodeInternalType(ref BlobReader blobReader)
{
ulong val = ReadPointerSized(ref blobReader);
return _provider.GetInternalType(_target, new TargetPointer(val));
}

private TType DecodeInternalModifiedType(ref BlobReader blobReader)
{
bool isRequired = blobReader.ReadByte() != 0;
ulong val = ReadPointerSized(ref blobReader);
TType unmodifiedType = DecodeType(ref blobReader);
return _provider.GetInternalModifiedType(_target, new TargetPointer(val), unmodifiedType, isRequired);
}

private TType DecodeTypeHandle(ref BlobReader blobReader, byte rawTypeKind, bool allowTypeSpecifications)
{
EntityHandle handle = blobReader.ReadTypeHandle();
if (!handle.IsNil)
{
switch (handle.Kind)
{
case HandleKind.TypeDefinition:
return _provider.GetTypeFromDefinition(_metadataReader, (TypeDefinitionHandle)handle, rawTypeKind);

case HandleKind.TypeReference:
return _provider.GetTypeFromReference(_metadataReader, (TypeReferenceHandle)handle, rawTypeKind);

case HandleKind.TypeSpecification:
if (!allowTypeSpecifications)
{
throw new BadImageFormatException("TypeSpecification handle not allowed in this context");
}
return _provider.GetTypeFromSpecification(_metadataReader, _genericContext, (TypeSpecificationHandle)handle, rawTypeKind);
}
}

throw new BadImageFormatException("Expected TypeDef, TypeRef, or TypeSpec handle");
}

private ulong ReadPointerSized(ref BlobReader blobReader)
{
return _pointerSize == 8 ? blobReader.ReadUInt64() : blobReader.ReadUInt32();
}

private static void CheckHeader(SignatureHeader header, SignatureKind expectedKind)
{
if (header.Kind != expectedKind)
{
throw new BadImageFormatException($"Expected signature header {expectedKind}, got {header.Kind} (raw 0x{header.RawValue:X2})");
}
}

private static void CheckMethodOrPropertyHeader(SignatureHeader header)
{
SignatureKind kind = header.Kind;
if (kind != SignatureKind.Method && kind != SignatureKind.Property)
{
throw new BadImageFormatException($"Expected Method or Property signature header, got {kind} (raw 0x{header.RawValue:X2})");
}
}
}
Loading
Loading