-
Notifications
You must be signed in to change notification settings - Fork 5.5k
[cDAC] RuntimeSignatureDecoder and centralized Signature contract (2/5) #127636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
6083571
WIP: RuntimeSignatureDecoder, centralized SignatureDecoder contract
c468dde
Align RuntimeSignatureDecoder with SRM and move GC decoding to StackWalk
112b4be
Address review feedback: split interface, drop dead code, require Met…
d5e314a
Capture Target in providers; document signature-based GC scanning
bfaf8af
Address PR review: handle StoredSigMethodDesc; cache GcSignatureTypeP…
d70e706
Resolve VAR/MVAR via instantiation; normalize enums to underlying pri…
ba37f41
Address PR review: visibility, dead state, doc fixes
b8179d0
Rename SignatureDecoder contract to Signature
e8f72c1
Address review feedback: data-first Signature.md + cross-ref tag
08ee9d5
address feedback
b696caf
remove all references to number of internal types
9872130
Handle array class contexts in GcSignatureTypeProvider
4eaf8ee
Use EcmaMetadataUtils for method token constants in GcScanner
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
...nostics.DataContractReader.Contracts/Contracts/Signature/IRuntimeSignatureTypeProvider.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | ||
| } |
331 changes: 331 additions & 0 deletions
331
...t.Diagnostics.DataContractReader.Contracts/Contracts/Signature/RuntimeSignatureDecoder.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | ||
|
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); | ||
|
max-charlamb marked this conversation as resolved.
Outdated
|
||
|
|
||
| _provider = provider; | ||
| _metadataReader = metadataReader; | ||
| _target = target; | ||
| _genericContext = genericContext; | ||
| _pointerSize = target.PointerSize; | ||
|
max-charlamb marked this conversation as resolved.
max-charlamb marked this conversation as resolved.
|
||
| } | ||
|
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"); | ||
|
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})"); | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.