< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 267
Coverable lines: 267
Total lines: 513
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 166
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%110%
DecodeValue(...)0%22220%
DecodeFixedArguments(...)0%440%
DecodeNamedArguments(...)0%880%
DecodeFixedArgumentType(...)0%41410%
DecodeNamedArgumentType(...)0%14140%
DecodeArgument(...)0%20200%
DecodeArrayArgument(...)0%880%
GetTypeFromHandle(...)0%440%
SkipType(...)0%45450%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/CustomAttributeDecoder.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Collections.Immutable;
 5
 6namespace System.Reflection.Metadata.Ecma335
 7{
 8    /// <summary>
 9    /// Decodes custom attribute blobs.
 10    /// </summary>
 11    internal readonly struct CustomAttributeDecoder<TType>
 12    {
 13        private readonly ICustomAttributeTypeProvider<TType> _provider;
 14        private readonly MetadataReader _reader;
 15
 16        public CustomAttributeDecoder(ICustomAttributeTypeProvider<TType> provider, MetadataReader reader)
 017        {
 018            _reader = reader;
 019            _provider = provider;
 020        }
 21
 22        public CustomAttributeValue<TType> DecodeValue(EntityHandle constructor, BlobHandle value)
 023        {
 24            BlobHandle signature;
 025            BlobHandle attributeOwningTypeSpec = default;
 026            switch (constructor.Kind)
 27            {
 28                case HandleKind.MethodDefinition:
 029                    MethodDefinition definition = _reader.GetMethodDefinition((MethodDefinitionHandle)constructor);
 030                    signature = definition.Signature;
 031                    break;
 32
 33                case HandleKind.MemberReference:
 034                    MemberReference reference = _reader.GetMemberReference((MemberReferenceHandle)constructor);
 035                    signature = reference.Signature;
 36
 37                    // If this is a generic attribute, we'll need its instantiation to decode the signatures
 038                    if (reference.Parent.Kind == HandleKind.TypeSpecification)
 039                    {
 040                        TypeSpecification genericOwner = _reader.GetTypeSpecification((TypeSpecificationHandle)reference
 041                        attributeOwningTypeSpec = genericOwner.Signature;
 042                    }
 043                    break;
 44
 45                default:
 046                    throw new BadImageFormatException();
 47            }
 48
 049            BlobReader signatureReader = _reader.GetBlobReader(signature);
 050            BlobReader valueReader = _reader.GetBlobReader(value);
 51
 052            ushort prolog = valueReader.ReadUInt16();
 053            if (prolog != 1)
 054            {
 055                throw new BadImageFormatException();
 56            }
 57
 058            SignatureHeader header = signatureReader.ReadSignatureHeader();
 059            if (header.Kind != SignatureKind.Method || header.IsGeneric)
 060            {
 061                throw new BadImageFormatException();
 62            }
 63
 064            int parameterCount = signatureReader.ReadCompressedInteger();
 065            SignatureTypeCode returnType = signatureReader.ReadSignatureTypeCode();
 066            if (returnType != SignatureTypeCode.Void)
 067            {
 068                throw new BadImageFormatException();
 69            }
 70
 071            BlobReader genericContextReader = default;
 072            if (!attributeOwningTypeSpec.IsNil)
 073            {
 74                // If this is a generic attribute, grab the instantiation arguments so that we can
 75                // interpret the constructor signature, should it refer to the generic context.
 076                genericContextReader = _reader.GetBlobReader(attributeOwningTypeSpec);
 077                if (genericContextReader.ReadSignatureTypeCode() == SignatureTypeCode.GenericTypeInstance)
 078                {
 079                    int kind = genericContextReader.ReadCompressedInteger();
 080                    if (kind != (int)SignatureTypeKind.Class && kind != (int)SignatureTypeKind.ValueType)
 081                    {
 082                        throw new BadImageFormatException();
 83                    }
 84
 085                    genericContextReader.ReadTypeHandle();
 86
 87                    // At this point, the reader points to the "GenArgCount Type Type*" part of the signature.
 088                }
 89                else
 090                {
 91                    // Some other invalid TypeSpec. Don't accidentally allow resolving generic parameters
 92                    // from the constructor signature into a broken blob.
 093                    genericContextReader = default;
 094                }
 095            }
 96
 097            ImmutableArray<CustomAttributeTypedArgument<TType>> fixedArguments = DecodeFixedArguments(ref signatureReade
 098            ImmutableArray<CustomAttributeNamedArgument<TType>> namedArguments = DecodeNamedArguments(ref valueReader);
 099            return new CustomAttributeValue<TType>(fixedArguments, namedArguments);
 0100        }
 101
 102        private ImmutableArray<CustomAttributeTypedArgument<TType>> DecodeFixedArguments(ref BlobReader signatureReader,
 0103        {
 0104            if (count == 0)
 0105            {
 0106                return ImmutableArray<CustomAttributeTypedArgument<TType>>.Empty;
 107            }
 108
 0109            var arguments = ImmutableArray.CreateBuilder<CustomAttributeTypedArgument<TType>>(count);
 110
 0111            for (int i = 0; i < count; i++)
 0112            {
 0113                ArgumentTypeInfo info = DecodeFixedArgumentType(ref signatureReader, genericContextReader);
 0114                arguments.Add(DecodeArgument(ref valueReader, info));
 0115            }
 116
 0117            return arguments.MoveToImmutable();
 0118        }
 119
 120        private ImmutableArray<CustomAttributeNamedArgument<TType>> DecodeNamedArguments(ref BlobReader valueReader)
 0121        {
 0122            int count = valueReader.ReadUInt16();
 0123            if (count == 0)
 0124            {
 0125                return ImmutableArray<CustomAttributeNamedArgument<TType>>.Empty;
 126            }
 127
 0128            var arguments = ImmutableArray.CreateBuilder<CustomAttributeNamedArgument<TType>>(count);
 0129            for (int i = 0; i < count; i++)
 0130            {
 0131                CustomAttributeNamedArgumentKind kind = (CustomAttributeNamedArgumentKind)valueReader.ReadSerializationT
 0132                if (kind != CustomAttributeNamedArgumentKind.Field && kind != CustomAttributeNamedArgumentKind.Property)
 0133                {
 0134                    throw new BadImageFormatException();
 135                }
 136
 0137                ArgumentTypeInfo info = DecodeNamedArgumentType(ref valueReader);
 0138                string? name = valueReader.ReadSerializedString();
 0139                CustomAttributeTypedArgument<TType> argument = DecodeArgument(ref valueReader, info);
 0140                arguments.Add(new CustomAttributeNamedArgument<TType>(name, kind, argument.Type, argument.Value));
 0141            }
 142
 0143            return arguments.MoveToImmutable();
 0144        }
 145
 146        private struct ArgumentTypeInfo
 147        {
 148            public TType Type;
 149            public TType ElementType;
 150            public SerializationTypeCode TypeCode;
 151            public SerializationTypeCode ElementTypeCode;
 152        }
 153
 154        // Decodes a fixed argument type of a custom attribute from its constructor signature.
 155        //
 156        // Note that we do not decode the full constructor signature using DecodeMethodSignature
 157        // but instead decode one parameter at a time as we read the value blob. This is both
 158        // better perf-wise, but even more important is that we can't actually reason about
 159        // a method signature with opaque TType values without adding some unnecessary chatter
 160        // with the provider.
 161        private ArgumentTypeInfo DecodeFixedArgumentType(ref BlobReader signatureReader, BlobReader genericContextReader
 0162        {
 0163            SignatureTypeCode signatureTypeCode = signatureReader.ReadSignatureTypeCode();
 164
 0165            var info = new ArgumentTypeInfo
 0166            {
 0167                TypeCode = (SerializationTypeCode)signatureTypeCode,
 0168            };
 169
 0170            switch (signatureTypeCode)
 171            {
 172                case SignatureTypeCode.Boolean:
 173                case SignatureTypeCode.Byte:
 174                case SignatureTypeCode.Char:
 175                case SignatureTypeCode.Double:
 176                case SignatureTypeCode.Int16:
 177                case SignatureTypeCode.Int32:
 178                case SignatureTypeCode.Int64:
 179                case SignatureTypeCode.SByte:
 180                case SignatureTypeCode.Single:
 181                case SignatureTypeCode.String:
 182                case SignatureTypeCode.UInt16:
 183                case SignatureTypeCode.UInt32:
 184                case SignatureTypeCode.UInt64:
 0185                    info.Type = _provider.GetPrimitiveType((PrimitiveTypeCode)signatureTypeCode);
 0186                    break;
 187
 188                case SignatureTypeCode.Object:
 0189                    info.TypeCode = SerializationTypeCode.TaggedObject;
 0190                    info.Type = _provider.GetPrimitiveType(PrimitiveTypeCode.Object);
 0191                    break;
 192
 193                case SignatureTypeCode.TypeHandle:
 194                    // Parameter is type def or ref and is only allowed to be System.Type or Enum.
 0195                    EntityHandle handle = signatureReader.ReadTypeHandle();
 0196                    info.Type = GetTypeFromHandle(handle);
 0197                    info.TypeCode = _provider.IsSystemType(info.Type) ? SerializationTypeCode.Type : (SerializationTypeC
 0198                    break;
 199
 200                case SignatureTypeCode.SZArray:
 0201                    if (isElementType)
 0202                    {
 203                        // jagged arrays are not allowed.
 0204                        throw new BadImageFormatException();
 205                    }
 206
 0207                    var elementInfo = DecodeFixedArgumentType(ref signatureReader, genericContextReader, isElementType: 
 0208                    info.ElementType = elementInfo.Type;
 0209                    info.ElementTypeCode = elementInfo.TypeCode;
 0210                    info.Type = _provider.GetSZArrayType(info.ElementType);
 0211                    break;
 212
 213                case SignatureTypeCode.GenericTypeParameter:
 0214                    if (genericContextReader.Length == 0)
 0215                    {
 0216                        throw new BadImageFormatException();
 217                    }
 218
 0219                    int parameterIndex = signatureReader.ReadCompressedInteger();
 0220                    int numGenericParameters = genericContextReader.ReadCompressedInteger();
 0221                    if (parameterIndex >= numGenericParameters)
 0222                    {
 0223                        throw new BadImageFormatException();
 224                    }
 225
 0226                    while (parameterIndex > 0)
 0227                    {
 0228                        SkipType(ref genericContextReader);
 0229                        parameterIndex--;
 0230                    }
 231
 0232                    return DecodeFixedArgumentType(ref genericContextReader, default, isElementType);
 233
 234                default:
 0235                    throw new BadImageFormatException();
 236            }
 237
 0238            return info;
 0239        }
 240
 241        private ArgumentTypeInfo DecodeNamedArgumentType(ref BlobReader valueReader, bool isElementType = false)
 0242        {
 0243            var info = new ArgumentTypeInfo
 0244            {
 0245                TypeCode = valueReader.ReadSerializationTypeCode(),
 0246            };
 247
 0248            switch (info.TypeCode)
 249            {
 250                case SerializationTypeCode.Boolean:
 251                case SerializationTypeCode.Byte:
 252                case SerializationTypeCode.Char:
 253                case SerializationTypeCode.Double:
 254                case SerializationTypeCode.Int16:
 255                case SerializationTypeCode.Int32:
 256                case SerializationTypeCode.Int64:
 257                case SerializationTypeCode.SByte:
 258                case SerializationTypeCode.Single:
 259                case SerializationTypeCode.String:
 260                case SerializationTypeCode.UInt16:
 261                case SerializationTypeCode.UInt32:
 262                case SerializationTypeCode.UInt64:
 0263                    info.Type = _provider.GetPrimitiveType((PrimitiveTypeCode)info.TypeCode);
 0264                    break;
 265
 266                case SerializationTypeCode.Type:
 0267                    info.Type = _provider.GetSystemType();
 0268                    break;
 269
 270                case SerializationTypeCode.TaggedObject:
 0271                    info.Type = _provider.GetPrimitiveType(PrimitiveTypeCode.Object);
 0272                    break;
 273
 274                case SerializationTypeCode.SZArray:
 0275                    if (isElementType)
 0276                    {
 277                        // jagged arrays are not allowed.
 0278                        throw new BadImageFormatException();
 279                    }
 280
 0281                    var elementInfo = DecodeNamedArgumentType(ref valueReader, isElementType: true);
 0282                    info.ElementType = elementInfo.Type;
 0283                    info.ElementTypeCode = elementInfo.TypeCode;
 0284                    info.Type = _provider.GetSZArrayType(info.ElementType);
 0285                    break;
 286
 287                case SerializationTypeCode.Enum:
 0288                    string? typeName = valueReader.ReadSerializedString();
 0289                    info.Type = _provider.GetTypeFromSerializedName(typeName!);
 0290                    info.TypeCode = (SerializationTypeCode)_provider.GetUnderlyingEnumType(info.Type);
 0291                    break;
 292
 293                default:
 0294                    throw new BadImageFormatException();
 295            }
 296
 0297            return info;
 0298        }
 299
 300        private CustomAttributeTypedArgument<TType> DecodeArgument(ref BlobReader valueReader, ArgumentTypeInfo info)
 0301        {
 0302            if (info.TypeCode == SerializationTypeCode.TaggedObject)
 0303            {
 0304                info = DecodeNamedArgumentType(ref valueReader);
 0305            }
 306
 307            // PERF_TODO: https://github.com/dotnet/runtime/issues/16551
 308            //   Cache /reuse common arguments to avoid boxing (small integers, true, false).
 309            object? value;
 0310            switch (info.TypeCode)
 311            {
 312                case SerializationTypeCode.Boolean:
 0313                    value = valueReader.ReadBoolean();
 0314                    break;
 315
 316                case SerializationTypeCode.Byte:
 0317                    value = valueReader.ReadByte();
 0318                    break;
 319
 320                case SerializationTypeCode.Char:
 0321                    value = valueReader.ReadChar();
 0322                    break;
 323
 324                case SerializationTypeCode.Double:
 0325                    value = valueReader.ReadDouble();
 0326                    break;
 327
 328                case SerializationTypeCode.Int16:
 0329                    value = valueReader.ReadInt16();
 0330                    break;
 331
 332                case SerializationTypeCode.Int32:
 0333                    value = valueReader.ReadInt32();
 0334                    break;
 335
 336                case SerializationTypeCode.Int64:
 0337                    value = valueReader.ReadInt64();
 0338                    break;
 339
 340                case SerializationTypeCode.SByte:
 0341                    value = valueReader.ReadSByte();
 0342                    break;
 343
 344                case SerializationTypeCode.Single:
 0345                    value = valueReader.ReadSingle();
 0346                    break;
 347
 348                case SerializationTypeCode.UInt16:
 0349                    value = valueReader.ReadUInt16();
 0350                    break;
 351
 352                case SerializationTypeCode.UInt32:
 0353                    value = valueReader.ReadUInt32();
 0354                    break;
 355
 356                case SerializationTypeCode.UInt64:
 0357                    value = valueReader.ReadUInt64();
 0358                    break;
 359
 360                case SerializationTypeCode.String:
 0361                    value = valueReader.ReadSerializedString();
 0362                    break;
 363
 364                case SerializationTypeCode.Type:
 0365                    string? typeName = valueReader.ReadSerializedString();
 0366                    value = _provider.GetTypeFromSerializedName(typeName!);
 0367                    break;
 368
 369                case SerializationTypeCode.SZArray:
 0370                    value = DecodeArrayArgument(ref valueReader, info);
 0371                    break;
 372
 373                default:
 0374                    throw new BadImageFormatException();
 375            }
 376
 0377            return new CustomAttributeTypedArgument<TType>(info.Type, value);
 0378        }
 379
 380        private ImmutableArray<CustomAttributeTypedArgument<TType>>? DecodeArrayArgument(ref BlobReader blobReader, Argu
 0381        {
 0382            int count = blobReader.ReadInt32();
 0383            if (count == -1)
 0384            {
 0385                return null;
 386            }
 387
 0388            if (count == 0)
 0389            {
 0390                return ImmutableArray<CustomAttributeTypedArgument<TType>>.Empty;
 391            }
 392
 0393            if (count < 0)
 0394            {
 0395                throw new BadImageFormatException();
 396            }
 397
 0398            var elementInfo = new ArgumentTypeInfo
 0399            {
 0400                Type = info.ElementType,
 0401                TypeCode = info.ElementTypeCode,
 0402            };
 403
 0404            var array = ImmutableArray.CreateBuilder<CustomAttributeTypedArgument<TType>>(count);
 405
 0406            for (int i = 0; i < count; i++)
 0407            {
 0408                array.Add(DecodeArgument(ref blobReader, elementInfo));
 0409            }
 410
 0411            return array.MoveToImmutable();
 0412        }
 413
 414        private TType GetTypeFromHandle(EntityHandle handle) =>
 0415            handle.Kind switch
 0416            {
 0417                HandleKind.TypeDefinition => _provider.GetTypeFromDefinition(_reader, (TypeDefinitionHandle)handle, 0),
 0418                HandleKind.TypeReference => _provider.GetTypeFromReference(_reader, (TypeReferenceHandle)handle, 0),
 0419                _ => throw new BadImageFormatException(SR.NotTypeDefOrRefHandle),
 0420            };
 421
 422        private static void SkipType(ref BlobReader blobReader)
 0423        {
 0424            int typeCode = blobReader.ReadCompressedInteger();
 425
 0426            switch (typeCode)
 427            {
 428                case (int)SignatureTypeCode.Boolean:
 429                case (int)SignatureTypeCode.Char:
 430                case (int)SignatureTypeCode.SByte:
 431                case (int)SignatureTypeCode.Byte:
 432                case (int)SignatureTypeCode.Int16:
 433                case (int)SignatureTypeCode.UInt16:
 434                case (int)SignatureTypeCode.Int32:
 435                case (int)SignatureTypeCode.UInt32:
 436                case (int)SignatureTypeCode.Int64:
 437                case (int)SignatureTypeCode.UInt64:
 438                case (int)SignatureTypeCode.Single:
 439                case (int)SignatureTypeCode.Double:
 440                case (int)SignatureTypeCode.IntPtr:
 441                case (int)SignatureTypeCode.UIntPtr:
 442                case (int)SignatureTypeCode.Object:
 443                case (int)SignatureTypeCode.String:
 444                case (int)SignatureTypeCode.Void:
 445                case (int)SignatureTypeCode.TypedReference:
 0446                    return;
 447
 448                case (int)SignatureTypeCode.Pointer:
 449                case (int)SignatureTypeCode.ByReference:
 450                case (int)SignatureTypeCode.Pinned:
 451                case (int)SignatureTypeCode.SZArray:
 0452                    SkipType(ref blobReader);
 0453                    return;
 454
 455                case (int)SignatureTypeCode.FunctionPointer:
 0456                    SignatureHeader header = blobReader.ReadSignatureHeader();
 0457                    if (header.IsGeneric)
 0458                    {
 0459                        blobReader.ReadCompressedInteger(); // arity
 0460                    }
 461
 0462                    int paramCount = blobReader.ReadCompressedInteger();
 0463                    SkipType(ref blobReader);
 0464                    for (int i = 0; i < paramCount; i++)
 0465                        SkipType(ref blobReader);
 0466                    return;
 467
 468                case (int)SignatureTypeCode.Array:
 0469                    SkipType(ref blobReader);
 0470                    blobReader.ReadCompressedInteger(); // rank
 0471                    int boundsCount = blobReader.ReadCompressedInteger();
 0472                    for (int i = 0; i < boundsCount; i++)
 0473                    {
 0474                        blobReader.ReadCompressedInteger();
 0475                    }
 0476                    int lowerBoundsCount = blobReader.ReadCompressedInteger();
 0477                    for (int i = 0; i < lowerBoundsCount; i++)
 0478                    {
 0479                        blobReader.ReadCompressedSignedInteger();
 0480                    }
 0481                    return;
 482
 483                case (int)SignatureTypeCode.RequiredModifier:
 484                case (int)SignatureTypeCode.OptionalModifier:
 0485                    blobReader.ReadTypeHandle();
 0486                    SkipType(ref blobReader);
 0487                    return;
 488
 489                case (int)SignatureTypeCode.GenericTypeInstance:
 0490                    SkipType(ref blobReader);
 0491                    int count = blobReader.ReadCompressedInteger();
 0492                    for (int i = 0; i < count; i++)
 0493                    {
 0494                        SkipType(ref blobReader);
 0495                    }
 0496                    return;
 497
 498                case (int)SignatureTypeCode.GenericTypeParameter:
 0499                    blobReader.ReadCompressedInteger();
 0500                    return;
 501
 502                case (int)SignatureTypeKind.Class:
 503                case (int)SignatureTypeKind.ValueType:
 0504                    SkipType(ref blobReader);
 0505                    break;
 506
 507                default:
 0508                    throw new BadImageFormatException();
 509            }
 0510        }
 511    }
 512}
 513