Skip to content
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

Add handling of generic attributes to CustomAttributeDecoder #57466

Closed
Closed
Changes from 1 commit
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 @@ -22,6 +22,7 @@ public CustomAttributeDecoder(ICustomAttributeTypeProvider<TType> provider, Meta
public CustomAttributeValue<TType> DecodeValue(EntityHandle constructor, BlobHandle value)
{
BlobHandle signature;
BlobHandle attributeOwningTypeSpec = default;
switch (constructor.Kind)
{
case HandleKind.MethodDefinition:
Expand All @@ -32,6 +33,13 @@ public CustomAttributeValue<TType> DecodeValue(EntityHandle constructor, BlobHan
case HandleKind.MemberReference:
MemberReference reference = _reader.GetMemberReference((MemberReferenceHandle)constructor);
signature = reference.Signature;

// If this is a generic attribute, we'll need its instantiation to decode the signatures
if (reference.Parent.Kind == HandleKind.TypeSpecification)
{
TypeSpecification genericOwner = _reader.GetTypeSpecification((TypeSpecificationHandle)reference.Parent);
attributeOwningTypeSpec = genericOwner.Signature;
}
break;

default:
Expand Down Expand Up @@ -60,12 +68,32 @@ public CustomAttributeValue<TType> DecodeValue(EntityHandle constructor, BlobHan
throw new BadImageFormatException();
}

ImmutableArray<CustomAttributeTypedArgument<TType>> fixedArguments = DecodeFixedArguments(ref signatureReader, ref valueReader, parameterCount);
BlobReader genericContextReader = default;
if (!attributeOwningTypeSpec.IsNil)
{
// If this is a generic attribute, grab the instantiation arguments so that we can
// interpret the constructor signature, should it refer to the generic context.
genericContextReader = _reader.GetBlobReader(attributeOwningTypeSpec);
if (genericContextReader.ReadSignatureTypeCode() == SignatureTypeCode.GenericTypeInstance)
MichalStrehovsky marked this conversation as resolved.
Show resolved Hide resolved
{
int kind = genericContextReader.ReadCompressedInteger();
if (kind != (int)SignatureTypeKind.Class && kind != (int)SignatureTypeKind.ValueType)
{
throw new BadImageFormatException();
}

genericContextReader.ReadTypeHandle();

// At this point, the reader points to the "GenArgCount Type Type*" part of the signature.
}
}

ImmutableArray<CustomAttributeTypedArgument<TType>> fixedArguments = DecodeFixedArguments(ref signatureReader, ref valueReader, parameterCount, genericContextReader);
ImmutableArray<CustomAttributeNamedArgument<TType>> namedArguments = DecodeNamedArguments(ref valueReader);
return new CustomAttributeValue<TType>(fixedArguments, namedArguments);
}

private ImmutableArray<CustomAttributeTypedArgument<TType>> DecodeFixedArguments(ref BlobReader signatureReader, ref BlobReader valueReader, int count)
private ImmutableArray<CustomAttributeTypedArgument<TType>> DecodeFixedArguments(ref BlobReader signatureReader, ref BlobReader valueReader, int count, BlobReader genericContextReader)
{
if (count == 0)
{
Expand All @@ -76,7 +104,7 @@ private ImmutableArray<CustomAttributeTypedArgument<TType>> DecodeFixedArguments

for (int i = 0; i < count; i++)
{
ArgumentTypeInfo info = DecodeFixedArgumentType(ref signatureReader);
ArgumentTypeInfo info = DecodeFixedArgumentType(ref signatureReader, genericContextReader);
arguments.Add(DecodeArgument(ref valueReader, info));
}

Expand Down Expand Up @@ -124,7 +152,7 @@ private struct ArgumentTypeInfo
// better perf-wise, but even more important is that we can't actually reason about
// a method signature with opaque TType values without adding some unnecessary chatter
// with the provider.
private ArgumentTypeInfo DecodeFixedArgumentType(ref BlobReader signatureReader, bool isElementType = false)
private ArgumentTypeInfo DecodeFixedArgumentType(ref BlobReader signatureReader, BlobReader genericContextReader, bool isElementType = false)
{
SignatureTypeCode signatureTypeCode = signatureReader.ReadSignatureTypeCode();

Expand Down Expand Up @@ -170,12 +198,28 @@ private ArgumentTypeInfo DecodeFixedArgumentType(ref BlobReader signatureReader,
throw new BadImageFormatException();
}

var elementInfo = DecodeFixedArgumentType(ref signatureReader, isElementType: true);
var elementInfo = DecodeFixedArgumentType(ref signatureReader, genericContextReader, isElementType: true);
info.ElementType = elementInfo.Type;
info.ElementTypeCode = elementInfo.TypeCode;
info.Type = _provider.GetSZArrayType(info.ElementType);
break;

case SignatureTypeCode.GenericTypeParameter:
int parameterIndex = signatureReader.ReadCompressedInteger();
int numGenericParameters = genericContextReader.ReadCompressedInteger();
if (parameterIndex >= numGenericParameters)
Copy link
Member

@krwq krwq Aug 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider also checking for negative parameterIndex and numGenericParameters

{
throw new BadImageFormatException();
}

while (parameterIndex > 0)
{
SkipType(ref genericContextReader);
parameterIndex--;
}

return DecodeFixedArgumentType(ref genericContextReader, default, isElementType);

default:
throw new BadImageFormatException();
}
Expand Down Expand Up @@ -363,5 +407,95 @@ private TType GetTypeFromHandle(EntityHandle handle) =>
HandleKind.TypeReference => _provider.GetTypeFromReference(_reader, (TypeReferenceHandle)handle, 0),
_ => throw new BadImageFormatException(SR.NotTypeDefOrRefHandle),
};

private static void SkipType(ref BlobReader blobReader)
{
int typeCode = blobReader.ReadCompressedInteger();

switch (typeCode)
{
case (int)SignatureTypeCode.Boolean:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't there any existing subroutine for checking simple types?

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;

case (int)SignatureTypeCode.Pointer:
case (int)SignatureTypeCode.ByReference:
case (int)SignatureTypeCode.Pinned:
case (int)SignatureTypeCode.SZArray:
SkipType(ref blobReader);
return;

case (int)SignatureTypeCode.FunctionPointer:
SignatureHeader header = blobReader.ReadSignatureHeader();
if (header.IsGeneric)
{
blobReader.ReadCompressedInteger(); // arity
}

int paramCount = blobReader.ReadCompressedInteger();
SkipType(ref blobReader);
for (int i = 0; i < paramCount; i++)
SkipType(ref blobReader);
return;

case (int)SignatureTypeCode.Array:
SkipType(ref blobReader);
blobReader.ReadCompressedInteger(); // rank
int boundsCount = blobReader.ReadCompressedInteger();
for (int i = 0; i < boundsCount; i++)
{
blobReader.ReadCompressedInteger();
}
int lowerBoundsCount = blobReader.ReadCompressedInteger();
for (int i = 0; i < lowerBoundsCount; i++)
{
blobReader.ReadCompressedSignedInteger();
}
return;

case (int)SignatureTypeCode.RequiredModifier:
case (int)SignatureTypeCode.OptionalModifier:
blobReader.ReadTypeHandle();
SkipType(ref blobReader);
return;

case (int)SignatureTypeCode.GenericTypeInstance:
SkipType(ref blobReader);
int count = blobReader.ReadCompressedInteger();
for (int i = 0; i < count; i++)
{
SkipType(ref blobReader);
}
return;

case (int)SignatureTypeCode.GenericTypeParameter:
blobReader.ReadCompressedInteger();
return;

case (int)SignatureTypeKind.Class:
case (int)SignatureTypeKind.ValueType:
SkipType(ref blobReader);
break;

default:
throw new BadImageFormatException();
}
}
}
}