.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
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

// 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();
}
}
}