mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
6.8 KiB
235 lines
6.8 KiB
// Licensed to the .NET Foundation under one or more agreements. |
|
// The .NET Foundation licenses this file to you under the MIT license. |
|
// See the LICENSE file in the project root for more information. |
|
|
|
using System; |
|
using System.Collections.Immutable; |
|
using System.Reflection.Metadata; |
|
|
|
namespace ICSharpCode.Decompiler.Metadata |
|
{ |
|
/// <summary> |
|
/// Decodes custom attribute blobs. |
|
/// </summary> |
|
internal readonly struct CustomAttributeDecoder<TType> |
|
{ |
|
// This is a stripped-down copy of SRM's internal CustomAttributeDecoder. |
|
// We need it to decode security declarations. |
|
|
|
private readonly ICustomAttributeTypeProvider<TType> _provider; |
|
private readonly MetadataReader _reader; |
|
private readonly bool _provideBoxingTypeInfo; |
|
|
|
public CustomAttributeDecoder(ICustomAttributeTypeProvider<TType> provider, MetadataReader reader, bool provideBoxingTypeInfo = false) |
|
{ |
|
_reader = reader; |
|
_provider = provider; |
|
_provideBoxingTypeInfo = provideBoxingTypeInfo; |
|
} |
|
|
|
public ImmutableArray<CustomAttributeNamedArgument<TType>> DecodeNamedArguments(ref BlobReader valueReader, int count) |
|
{ |
|
var arguments = ImmutableArray.CreateBuilder<CustomAttributeNamedArgument<TType>>(count); |
|
for (int i = 0; i < count; i++) |
|
{ |
|
CustomAttributeNamedArgumentKind kind = (CustomAttributeNamedArgumentKind)valueReader.ReadSerializationTypeCode(); |
|
if (kind != CustomAttributeNamedArgumentKind.Field && kind != CustomAttributeNamedArgumentKind.Property) |
|
{ |
|
throw new BadImageFormatException(); |
|
} |
|
|
|
ArgumentTypeInfo info = DecodeNamedArgumentType(ref valueReader); |
|
string name = valueReader.ReadSerializedString(); |
|
CustomAttributeTypedArgument<TType> argument = DecodeArgument(ref valueReader, info); |
|
arguments.Add(new CustomAttributeNamedArgument<TType>(name, kind, argument.Type, argument.Value)); |
|
} |
|
|
|
return arguments.MoveToImmutable(); |
|
} |
|
|
|
private struct ArgumentTypeInfo |
|
{ |
|
public TType Type; |
|
public TType ElementType; |
|
public SerializationTypeCode TypeCode; |
|
public SerializationTypeCode ElementTypeCode; |
|
} |
|
|
|
private ArgumentTypeInfo DecodeNamedArgumentType(ref BlobReader valueReader, bool isElementType = false) |
|
{ |
|
var info = new ArgumentTypeInfo { |
|
TypeCode = valueReader.ReadSerializationTypeCode(), |
|
}; |
|
|
|
switch (info.TypeCode) |
|
{ |
|
case SerializationTypeCode.Boolean: |
|
case SerializationTypeCode.Byte: |
|
case SerializationTypeCode.Char: |
|
case SerializationTypeCode.Double: |
|
case SerializationTypeCode.Int16: |
|
case SerializationTypeCode.Int32: |
|
case SerializationTypeCode.Int64: |
|
case SerializationTypeCode.SByte: |
|
case SerializationTypeCode.Single: |
|
case SerializationTypeCode.String: |
|
case SerializationTypeCode.UInt16: |
|
case SerializationTypeCode.UInt32: |
|
case SerializationTypeCode.UInt64: |
|
info.Type = _provider.GetPrimitiveType((PrimitiveTypeCode)info.TypeCode); |
|
break; |
|
|
|
case SerializationTypeCode.Type: |
|
info.Type = _provider.GetSystemType(); |
|
break; |
|
|
|
case SerializationTypeCode.TaggedObject: |
|
info.Type = _provider.GetPrimitiveType(PrimitiveTypeCode.Object); |
|
break; |
|
|
|
case SerializationTypeCode.SZArray: |
|
if (isElementType) |
|
{ |
|
// jagged arrays are not allowed. |
|
throw new BadImageFormatException(); |
|
} |
|
|
|
var elementInfo = DecodeNamedArgumentType(ref valueReader, isElementType: true); |
|
info.ElementType = elementInfo.Type; |
|
info.ElementTypeCode = elementInfo.TypeCode; |
|
info.Type = _provider.GetSZArrayType(info.ElementType); |
|
break; |
|
|
|
case SerializationTypeCode.Enum: |
|
string typeName = valueReader.ReadSerializedString(); |
|
info.Type = _provider.GetTypeFromSerializedName(typeName); |
|
info.TypeCode = (SerializationTypeCode)_provider.GetUnderlyingEnumType(info.Type); |
|
break; |
|
|
|
default: |
|
throw new BadImageFormatException(); |
|
} |
|
|
|
return info; |
|
} |
|
|
|
private CustomAttributeTypedArgument<TType> DecodeArgument(ref BlobReader valueReader, ArgumentTypeInfo info) |
|
{ |
|
var outer = info; |
|
if (info.TypeCode == SerializationTypeCode.TaggedObject) |
|
{ |
|
info = DecodeNamedArgumentType(ref valueReader); |
|
} |
|
|
|
// PERF_TODO: https://github.com/dotnet/corefx/issues/6533 |
|
// Cache /reuse common arguments to avoid boxing (small integers, true, false). |
|
object value; |
|
switch (info.TypeCode) |
|
{ |
|
case SerializationTypeCode.Boolean: |
|
value = valueReader.ReadBoolean(); |
|
break; |
|
|
|
case SerializationTypeCode.Byte: |
|
value = valueReader.ReadByte(); |
|
break; |
|
|
|
case SerializationTypeCode.Char: |
|
value = valueReader.ReadChar(); |
|
break; |
|
|
|
case SerializationTypeCode.Double: |
|
value = valueReader.ReadDouble(); |
|
break; |
|
|
|
case SerializationTypeCode.Int16: |
|
value = valueReader.ReadInt16(); |
|
break; |
|
|
|
case SerializationTypeCode.Int32: |
|
value = valueReader.ReadInt32(); |
|
break; |
|
|
|
case SerializationTypeCode.Int64: |
|
value = valueReader.ReadInt64(); |
|
break; |
|
|
|
case SerializationTypeCode.SByte: |
|
value = valueReader.ReadSByte(); |
|
break; |
|
|
|
case SerializationTypeCode.Single: |
|
value = valueReader.ReadSingle(); |
|
break; |
|
|
|
case SerializationTypeCode.UInt16: |
|
value = valueReader.ReadUInt16(); |
|
break; |
|
|
|
case SerializationTypeCode.UInt32: |
|
value = valueReader.ReadUInt32(); |
|
break; |
|
|
|
case SerializationTypeCode.UInt64: |
|
value = valueReader.ReadUInt64(); |
|
break; |
|
|
|
case SerializationTypeCode.String: |
|
value = valueReader.ReadSerializedString(); |
|
break; |
|
|
|
case SerializationTypeCode.Type: |
|
string typeName = valueReader.ReadSerializedString(); |
|
value = _provider.GetTypeFromSerializedName(typeName); |
|
break; |
|
|
|
case SerializationTypeCode.SZArray: |
|
value = DecodeArrayArgument(ref valueReader, info); |
|
break; |
|
|
|
default: |
|
throw new BadImageFormatException(); |
|
} |
|
|
|
if (_provideBoxingTypeInfo && outer.TypeCode == SerializationTypeCode.TaggedObject) |
|
{ |
|
return new CustomAttributeTypedArgument<TType>(outer.Type, new CustomAttributeTypedArgument<TType>(info.Type, value)); |
|
} |
|
|
|
return new CustomAttributeTypedArgument<TType>(info.Type, value); |
|
} |
|
|
|
private ImmutableArray<CustomAttributeTypedArgument<TType>>? DecodeArrayArgument(ref BlobReader blobReader, ArgumentTypeInfo info) |
|
{ |
|
int count = blobReader.ReadInt32(); |
|
if (count == -1) |
|
{ |
|
return null; |
|
} |
|
|
|
if (count == 0) |
|
{ |
|
return ImmutableArray<CustomAttributeTypedArgument<TType>>.Empty; |
|
} |
|
|
|
if (count < 0) |
|
{ |
|
throw new BadImageFormatException(); |
|
} |
|
|
|
var elementInfo = new ArgumentTypeInfo { |
|
Type = info.ElementType, |
|
TypeCode = info.ElementTypeCode, |
|
}; |
|
|
|
var array = ImmutableArray.CreateBuilder<CustomAttributeTypedArgument<TType>>(count); |
|
|
|
for (int i = 0; i < count; i++) |
|
{ |
|
array.Add(DecodeArgument(ref blobReader, elementInfo)); |
|
} |
|
|
|
return array.MoveToImmutable(); |
|
} |
|
} |
|
} |