context: /*
      Copyright (C) 2014-2019 de4dot@gmail.com
  
      This file is part of dnSpy
  
      dnSpy is free software: you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation, either version 3 of the License, or
      (at your option) any later version.
  
      dnSpy is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
  
      You should have received a copy of the GNU General Public License
      along with dnSpy.  If not, see <http://www.gnu.org/licenses/>.
  */
  
  using System;
  using System.Collections.ObjectModel;
  using System.Diagnostics;
  using System.Linq;
  using System.Runtime.InteropServices;
  
  namespace dnSpy.Debugger.DotNet.Metadata {
  	static class CustomAttributesHelper {
  		public static bool IsDefined(ReadOnlyCollection<DmdCustomAttributeData> customAttributes, string attributeTypeFullName) {
  			for (int i = 0; i < customAttributes.Count; i++) {
  				if (customAttributes[i].AttributeType.FullName == attributeTypeFullName)
  					return true;
  			}
  			return false;
  		}
  
  		public static bool IsDefined(ReadOnlyCollection<DmdCustomAttributeData> customAttributes, DmdType? attributeType) {
  			for (int i = 0; i < customAttributes.Count; i++) {
  				if (DmdMemberInfoEqualityComparer.DefaultType.Equals(customAttributes[i].AttributeType, attributeType))
  					return true;
  			}
  			return false;
  		}
  
  		public static bool IsDefined(DmdType type, string attributeTypeFullName, bool inherit) {
  			for (DmdType? currentType = type; !(currentType is null); currentType = currentType.BaseType) {
  				var customAttributes = currentType.GetCustomAttributesData();
  				for (int i = 0; i < customAttributes.Count; i++) {
  					var ca = customAttributes[i];
  					if ((object)currentType != type && ca.IsPseudoCustomAttribute)
  						continue;
  					if (ca.AttributeType.FullName == attributeTypeFullName)
  						return true;
  				}
  				if (!inherit)
  					break;
  			}
  			return false;
  		}
  
  		public static bool IsDefined(DmdType type, DmdType? attributeType, bool inherit) {
  			for (DmdType? currentType = type; !(currentType is null); currentType = currentType.BaseType) {
  				var customAttributes = currentType.GetCustomAttributesData();
  				for (int i = 0; i < customAttributes.Count; i++) {
  					var ca = customAttributes[i];
  					if ((object)currentType != type && ca.IsPseudoCustomAttribute)
  						continue;
  					if (DmdMemberInfoEqualityComparer.DefaultType.Equals(ca.AttributeType, attributeType))
  						return true;
  				}
  				if (!inherit)
  					break;
  			}
  			return false;
  		}
  
  		public static bool IsDefined(DmdMethodInfo method, string attributeTypeFullName, bool inherit) {
  			for (DmdMethodInfo? currentMethod = method; !(currentMethod is null); currentMethod = currentMethod.GetParentDefinition()) {
  				var customAttributes = currentMethod.GetCustomAttributesData();
  				for (int i = 0; i < customAttributes.Count; i++) {
  					var ca = customAttributes[i];
  					if ((object)currentMethod != method && ca.IsPseudoCustomAttribute)
  						continue;
  					if (ca.AttributeType.FullName == attributeTypeFullName)
  						return true;
  				}
  				if (!inherit)
  					break;
  			}
  			return false;
  		}
  
  		public static bool IsDefined(DmdMethodInfo method, DmdType? attributeType, bool inherit) {
  			for (DmdMethodInfo? currentMethod = method; !(currentMethod is null); currentMethod = currentMethod.GetParentDefinition()) {
  				var customAttributes = currentMethod.GetCustomAttributesData();
  				for (int i = 0; i < customAttributes.Count; i++) {
  					var ca = customAttributes[i];
  					if ((object)currentMethod != method && ca.IsPseudoCustomAttribute)
  						continue;
  					if (DmdMemberInfoEqualityComparer.DefaultType.Equals(ca.AttributeType, attributeType))
  						return true;
  				}
  				if (!inherit)
  					break;
  			}
  			return false;
  		}
  
  		public static DmdCustomAttributeData? Find(ReadOnlyCollection<DmdCustomAttributeData> customAttributes, string attributeTypeFullName) {
  			for (int i = 0; i < customAttributes.Count; i++) {
  				var ca = customAttributes[i];
  				if (ca.AttributeType.FullName == attributeTypeFullName)
  					return ca;
  			}
  			return null;
  		}
  
  		public static DmdCustomAttributeData? Find(ReadOnlyCollection<DmdCustomAttributeData> customAttributes, DmdType? attributeType) {
  			for (int i = 0; i < customAttributes.Count; i++) {
  				var ca = customAttributes[i];
  				if (DmdMemberInfoEqualityComparer.DefaultType.Equals(ca.AttributeType, attributeType))
  					return ca;
  			}
  			return null;
  		}
  
  		public static DmdCustomAttributeData? Find(DmdType type, string attributeTypeFullName, bool inherit) {
  			for (DmdType? currentType = type; !(currentType is null); currentType = currentType.BaseType) {
  				var customAttributes = currentType.GetCustomAttributesData();
  				for (int i = 0; i < customAttributes.Count; i++) {
  					var ca = customAttributes[i];
  					if ((object)currentType != type && ca.IsPseudoCustomAttribute)
  						continue;
  					if (ca.AttributeType.FullName == attributeTypeFullName)
  						return ca;
  				}
  				if (!inherit)
  					break;
  			}
  			return null;
  		}
  
  		public static DmdCustomAttributeData? Find(DmdType type, DmdType? attributeType, bool inherit) {
  			for (DmdType? currentType = type; !(currentType is null); currentType = currentType.BaseType) {
  				var customAttributes = currentType.GetCustomAttributesData();
  				for (int i = 0; i < customAttributes.Count; i++) {
  					var ca = customAttributes[i];
  					if ((object)currentType != type && ca.IsPseudoCustomAttribute)
  						continue;
  					if (DmdMemberInfoEqualityComparer.DefaultType.Equals(ca.AttributeType, attributeType))
  						return ca;
  				}
  				if (!inherit)
  					break;
  			}
  			return null;
  		}
  
  		public static DmdCustomAttributeData? Find(DmdMethodInfo method, string attributeTypeFullName, bool inherit) {
  			for (DmdMethodInfo? currentMethod = method; !(currentMethod is null); currentMethod = currentMethod.GetParentDefinition()) {
  				var customAttributes = currentMethod.GetCustomAttributesData();
  				for (int i = 0; i < customAttributes.Count; i++) {
  					var ca = customAttributes[i];
  					if ((object)currentMethod != method && ca.IsPseudoCustomAttribute)
  						continue;
  					if (ca.AttributeType.FullName == attributeTypeFullName)
  						return ca;
  				}
  				if (!inherit)
  					break;
  			}
  			return null;
  		}
  
  		public static DmdCustomAttributeData? Find(DmdMethodInfo method, DmdType? attributeType, bool inherit) {
  			for (DmdMethodInfo? currentMethod = method; !(currentMethod is null); currentMethod = currentMethod.GetParentDefinition()) {
  				var customAttributes = currentMethod.GetCustomAttributesData();
  				for (int i = 0; i < customAttributes.Count; i++) {
  					var ca = customAttributes[i];
  					if ((object)currentMethod != method && ca.IsPseudoCustomAttribute)
  						continue;
  					if (DmdMemberInfoEqualityComparer.DefaultType.Equals(ca.AttributeType, attributeType))
  						return ca;
  				}
  				if (!inherit)
  					break;
  			}
  			return null;
  		}
  
  		readonly struct SecurityAttributeInfo {
  			public int Count { get; }
  
  			public SecurityAttributeInfo(ReadOnlyCollection<DmdCustomAttributeData> securityAttributes) => Count = securityAttributes.Count;
  
  			public void CopyTo(DmdCustomAttributeData[] destination, ref int index, ReadOnlyCollection<DmdCustomAttributeData> securityAttributes) {
  				if (Count == 0)
  					return;
  				for (int i = 0; i < securityAttributes.Count; i++) {
  					var sa = securityAttributes[i];
  					// Reflection uses the first public constructor it finds and uses empty ctor args and named args
  					var ctor = sa.AttributeType.GetConstructors().FirstOrDefault() ?? sa.Constructor;
  					destination[index++] = new DmdCustomAttributeData(ctor, null, null, isPseudoCustomAttribute: true);
  				}
  			}
  		}
  
  		readonly struct SerializableAttributeInfo {
  			public int Count => !(ctor is null) ? 1 : 0;
  			readonly DmdConstructorInfo? ctor;
  
  			public SerializableAttributeInfo(DmdType type) {
  				if ((type.Attributes & DmdTypeAttributes.Serializable) != 0) {
  					var caType = type.AppDomain.GetWellKnownType(DmdWellKnownType.System_SerializableAttribute, isOptional: true);
  					ctor = caType?.GetConstructor(Array.Empty<DmdType>());
  					Debug.Assert(caType is null || !(ctor is null));
  				}
  				else
  					ctor = null;
  			}
  
  			public void CopyTo(DmdCustomAttributeData[] destination, ref int index) {
  				if (Count == 0)
  					return;
  				destination[index++] = new DmdCustomAttributeData(ctor!, null, null, isPseudoCustomAttribute: true);
  			}
  		}
  
  		readonly struct ComImportAttributeInfo {
  			public int Count => !(ctor is null) ? 1 : 0;
  			readonly DmdConstructorInfo? ctor;
  
  			public ComImportAttributeInfo(DmdType type) {
  				if (type.IsImport) {
  					var caType = type.AppDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_ComImportAttribute, isOptional: true);
  					ctor = caType?.GetConstructor(Array.Empty<DmdType>());
  					Debug.Assert(caType is null || !(ctor is null));
  				}
  				else
  					ctor = null;
  			}
  
  			public void CopyTo(DmdCustomAttributeData[] destination, ref int index) {
  				if (Count == 0)
  					return;
  				destination[index++] = new DmdCustomAttributeData(ctor!, null, null, isPseudoCustomAttribute: true);
  			}
  		}
  
  		readonly struct MarshalAsAttributeInfo {
  			public int Count => !(ctor is null) ? 1 : 0;
  			readonly DmdConstructorInfo? ctor;
  
  			public MarshalAsAttributeInfo(DmdFieldInfo field, DmdMarshalType? marshalType) => ctor = Initialize(field.AppDomain, marshalType);
  			public MarshalAsAttributeInfo(DmdParameterInfo parameter, DmdMarshalType? marshalType) => ctor = Initialize(parameter.Member.AppDomain, marshalType);
  
  			static DmdConstructorInfo? Initialize(DmdAppDomain appDomain, DmdMarshalType? marshalType) {
  				if (marshalType is null)
  					return null;
  				var caType = appDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_MarshalAsAttribute, isOptional: true);
  				var unmanagedTypeType = appDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_UnmanagedType, isOptional: true);
  				var varEnumType = appDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_VarEnum, isOptional: true);
  				if (caType is null || unmanagedTypeType is null || varEnumType is null)
  					return null;
  				var ctor = caType.GetConstructor(new[] { unmanagedTypeType });
  				Debug.Assert(!(ctor is null));
  				return ctor;
  			}
  
  			public void CopyTo(DmdCustomAttributeData[] destination, ref int index, DmdMarshalType? marshalType) {
  				if (Count == 0)
  					return;
  				int argsCount = 5;
  				if (!(marshalType!.MarshalType is null))
  					argsCount++;
  				if (!(marshalType.MarshalTypeRef is null))
  					argsCount++;
  				if (!(marshalType.MarshalCookie is null))
  					argsCount++;
  				if (!(marshalType.SafeArrayUserDefinedSubType is null))
  					argsCount++;
  				var type = ctor!.ReflectedType!;
  				var appDomain = type.AppDomain;
  				var unmanagedTypeType = appDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_UnmanagedType);
  				var varEnumType = appDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_VarEnum);
  				var namedArgs = new DmdCustomAttributeNamedArgument[argsCount];
  				int w = 0;
  				namedArgs[w++] = new DmdCustomAttributeNamedArgument(type.GetField("ArraySubType"), new DmdCustomAttributeTypedArgument(unmanagedTypeType, (int)marshalType.ArraySubType));
  				namedArgs[w++] = new DmdCustomAttributeNamedArgument(type.GetField("SizeParamIndex"), new DmdCustomAttributeTypedArgument(appDomain.System_Int16, marshalType.SizeParamIndex));
  				namedArgs[w++] = new DmdCustomAttributeNamedArgument(type.GetField("SizeConst"), new DmdCustomAttributeTypedArgument(appDomain.System_Int32, marshalType.SizeConst));
  				namedArgs[w++] = new DmdCustomAttributeNamedArgument(type.GetField("IidParameterIndex"), new DmdCustomAttributeTypedArgument(appDomain.System_Int32, marshalType.IidParameterIndex));
  				namedArgs[w++] = new DmdCustomAttributeNamedArgument(type.GetField("SafeArraySubType"), new DmdCustomAttributeTypedArgument(varEnumType, (int)marshalType.SafeArraySubType));
  				if (!(marshalType.MarshalType is null))
  					namedArgs[w++] = new DmdCustomAttributeNamedArgument(type.GetField("MarshalType"), new DmdCustomAttributeTypedArgument(appDomain.System_String, marshalType.MarshalType));
  				if (!(marshalType.MarshalTypeRef is null))
  					namedArgs[w++] = new DmdCustomAttributeNamedArgument(type.GetField("MarshalTypeRef"), new DmdCustomAttributeTypedArgument(appDomain.System_Type, marshalType.MarshalTypeRef));
  				if (!(marshalType.MarshalCookie is null))
  					namedArgs[w++] = new DmdCustomAttributeNamedArgument(type.GetField("MarshalCookie"), new DmdCustomAttributeTypedArgument(appDomain.System_String, marshalType.MarshalCookie));
  				if (!(marshalType.SafeArrayUserDefinedSubType is null))
  					namedArgs[w++] = new DmdCustomAttributeNamedArgument(type.GetField("SafeArrayUserDefinedSubType"), new DmdCustomAttributeTypedArgument(appDomain.System_Type, marshalType.SafeArrayUserDefinedSubType));
  				if (namedArgs.Length != w)
  					throw new InvalidOperationException();
  				var ctorArgs = new[] { new DmdCustomAttributeTypedArgument(unmanagedTypeType, (int)marshalType.Value) };
  				destination[index++] = new DmdCustomAttributeData(ctor, ctorArgs, namedArgs, isPseudoCustomAttribute: true);
  			}
  		}
  
  		readonly struct FieldOffsetAttributeInfo {
  			public int Count => !(ctor is null) ? 1 : 0;
  			readonly DmdConstructorInfo? ctor;
  			readonly int offset;
  
  			public FieldOffsetAttributeInfo(DmdFieldInfo field, uint? offset) {
  				if (!(offset is null)) {
  					this.offset = (int)offset.Value;
  					var caType = field.AppDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_FieldOffsetAttribute, isOptional: true);
  					ctor = caType?.GetConstructor(new[] { field.AppDomain.System_Int32 });
  					Debug.Assert(caType is null || !(ctor is null));
  				}
  				else {
  					ctor = null;
  					this.offset = 0;
  				}
  			}
  
  			public void CopyTo(DmdCustomAttributeData[] destination, ref int index) {
  				if (Count == 0)
  					return;
  				var ctorArgs = new[] { new DmdCustomAttributeTypedArgument(ctor!.AppDomain.System_Int32, offset) };
  				destination[index++] = new DmdCustomAttributeData(ctor, ctorArgs, null, isPseudoCustomAttribute: true);
  			}
  		}
  
  		readonly struct NonSerializedAttributeInfo {
  			public int Count => !(ctor is null) ? 1 : 0;
  			readonly DmdConstructorInfo? ctor;
  
  			public NonSerializedAttributeInfo(DmdFieldInfo field) {
  				if ((field.Attributes & DmdFieldAttributes.NotSerialized) != 0) {
  					var caType = field.AppDomain.GetWellKnownType(DmdWellKnownType.System_NonSerializedAttribute, isOptional: true);
  					ctor = caType?.GetConstructor(Array.Empty<DmdType>());
  					Debug.Assert(caType is null || !(ctor is null));
  				}
  				else
  					ctor = null;
  			}
  
  			public void CopyTo(DmdCustomAttributeData[] destination, ref int index) {
  				if (Count == 0)
  					return;
  				destination[index++] = new DmdCustomAttributeData(ctor!, null, null, isPseudoCustomAttribute: true);
  			}
  		}
  
  		readonly struct DllImportAttributeInfo {
  			public int Count => !(ctor is null) ? 1 : 0;
  			readonly DmdConstructorInfo? ctor;
  
  			public DllImportAttributeInfo(DmdMethodInfo method, in DmdImplMap? implMap) {
  				if (!(implMap is null)) {
  					var appDomain = method.AppDomain;
  					var caType = appDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_DllImportAttribute, isOptional: true);
  					var charSetType = appDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_CharSet, isOptional: true);
  					var callingConventionType = appDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_CallingConvention, isOptional: true);
  					if (charSetType is null || callingConventionType is null || caType is null)
  						ctor = null;
  					else {
  						ctor = caType.GetConstructor(DmdBindingFlags.Public | DmdBindingFlags.NonPublic | DmdBindingFlags.Instance, new[] { appDomain.System_String });
  						Debug.Assert(!(ctor is null));
  					}
  				}
  				else
  					ctor = null;
  			}
  
  			public void CopyTo(DmdCustomAttributeData[] destination, ref int index, DmdMethodInfo method, in DmdImplMap? implMap) {
  				if (Count == 0)
  					return;
  
  				var appDomain = ctor!.AppDomain;
  				var im = implMap!.Value;
  				var attributes = im.Attributes;
  				var charSetType = appDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_CharSet);
  				var callingConventionType = appDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_CallingConvention);
  
  				CharSet charSet;
  				switch (attributes & DmdPInvokeAttributes.CharSetMask) {
  				default:
  				case DmdPInvokeAttributes.CharSetNotSpec:	charSet = CharSet.None; break;
  				case DmdPInvokeAttributes.CharSetAnsi:		charSet = CharSet.Ansi; break;
  				case DmdPInvokeAttributes.CharSetUnicode:	charSet = CharSet.Unicode; break;
  				case DmdPInvokeAttributes.CharSetAuto:		charSet = CharSet.Auto; break;
  				}
  
  				CallingConvention callingConvention;
  				switch (attributes & DmdPInvokeAttributes.CallConvMask) {
  				case DmdPInvokeAttributes.CallConvWinapi:		callingConvention = CallingConvention.Winapi; break;
  				case DmdPInvokeAttributes.CallConvCdecl:		callingConvention = CallingConvention.Cdecl; break;
  				case DmdPInvokeAttributes.CallConvStdcall:		callingConvention = CallingConvention.StdCall; break;
  				case DmdPInvokeAttributes.CallConvThiscall:		callingConvention = CallingConvention.ThisCall; break;
  				case DmdPInvokeAttributes.CallConvFastcall:		callingConvention = CallingConvention.FastCall; break;
  				default:										callingConvention = CallingConvention.Cdecl; break;
  				}
  
  				var ctorArgs = new[] { new DmdCustomAttributeTypedArgument(appDomain.System_String, im.Module) };
  				var type = ctor.ReflectedType!;
  				var namedArgs = new DmdCustomAttributeNamedArgument[8] {
  					new DmdCustomAttributeNamedArgument(type.GetField("EntryPoint"), new DmdCustomAttributeTypedArgument(appDomain.System_String, im.Name)),
  					new DmdCustomAttributeNamedArgument(type.GetField("CharSet"), new DmdCustomAttributeTypedArgument(charSetType, (int)charSet)),
  					new DmdCustomAttributeNamedArgument(type.GetField("ExactSpelling"), new DmdCustomAttributeTypedArgument(appDomain.System_Boolean, (attributes & DmdPInvokeAttributes.NoMangle) != 0)),
  					new DmdCustomAttributeNamedArgument(type.GetField("SetLastError"), new DmdCustomAttributeTypedArgument(appDomain.System_Boolean, (attributes & DmdPInvokeAttributes.SupportsLastError) != 0)),
  					new DmdCustomAttributeNamedArgument(type.GetField("PreserveSig"), new DmdCustomAttributeTypedArgument(appDomain.System_Boolean, method.IsPreserveSig)),
  					new DmdCustomAttributeNamedArgument(type.GetField("CallingConvention"), new DmdCustomAttributeTypedArgument(callingConventionType, (int)callingConvention)),
  					new DmdCustomAttributeNamedArgument(type.GetField("BestFitMapping"), new DmdCustomAttributeTypedArgument(appDomain.System_Boolean, (attributes & DmdPInvokeAttributes.BestFitMask) == DmdPInvokeAttributes.BestFitEnabled)),
  					new DmdCustomAttributeNamedArgument(type.GetField("ThrowOnUnmappableChar"), new DmdCustomAttributeTypedArgument(appDomain.System_Boolean, (attributes & DmdPInvokeAttributes.ThrowOnUnmappableCharMask) == DmdPInvokeAttributes.ThrowOnUnmappableCharEnabled)),
  				};
  				destination[index++] = new DmdCustomAttributeData(ctor, ctorArgs, namedArgs, isPseudoCustomAttribute: true);
  			}
  		}
  
  		readonly struct PreserveSigAttributeInfo {
  			public int Count => !(ctor is null) ? 1 : 0;
  			readonly DmdConstructorInfo? ctor;
  
  			public PreserveSigAttributeInfo(DmdMethodInfo method) {
  				if ((method.MethodImplementationFlags & DmdMethodImplAttributes.PreserveSig) != 0) {
  					var caType = method.AppDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_PreserveSigAttribute, isOptional: true);
  					ctor = caType?.GetConstructor(Array.Empty<DmdType>());
  					Debug.Assert(caType is null || !(ctor is null));
  				}
  				else
  					ctor = null;
  			}
  
  			public void CopyTo(DmdCustomAttributeData[] destination, ref int index) {
  
gt: 				if (Count == 0)