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