context: using System;
  using System.Collections.Concurrent;
  using System.Collections.Generic;
  using System.Collections.ObjectModel;
  using System.Diagnostics;
  using System.IO;
  using System.Linq;
  using System.Runtime.CompilerServices;
  using System.Runtime.InteropServices;
  using System.Threading;
  
  using ICSharpCode.Decompiler;
  using ICSharpCode.Decompiler.Ast.Transforms;
  using ICSharpCode.Decompiler.ILAst;
  using ICSharpCode.NRefactory.CSharp;
  using ICSharpCode.NRefactory.Utils;
  using Mono.Cecil;
  using Mono.Cecil.Cil;
  
  namespace ICSharpCode.Decompiler.Ast
  {
  	using Ast = ICSharpCode.NRefactory.CSharp;
  	using ClassType = ICSharpCode.NRefactory.TypeSystem.ClassType;
  	using VarianceModifier = ICSharpCode.NRefactory.TypeSystem.VarianceModifier;
  	
  	[Flags]
  	public enum ConvertTypeOptions
  	{
  		None = 0,
  		IncludeNamespace = 1,
  		IncludeTypeParameterDefinitions = 2
  	}
  	
  	public class AstBuilder : ICodeMappings
  	{
  		DecompilerContext context;
  		CompilationUnit astCompileUnit = new CompilationUnit();
  		Dictionary<string, NamespaceDeclaration> astNamespaces = new Dictionary<string, NamespaceDeclaration>();
  		bool transformationsHaveRun;
  		
  		public AstBuilder(DecompilerContext context)
  		{
  			if (context == null)
  				throw new ArgumentNullException("context");
  			this.context = context;
  			this.DecompileMethodBodies = true;
  			
  			this.LocalVariables = new ConcurrentDictionary<int, IEnumerable<ILVariable>>();
  		}
  		
  		public static bool MemberIsHidden(MemberReference member, DecompilerSettings settings)
  		{
  			MethodDefinition method = member as MethodDefinition;
  			if (method != null) {
  				if (method.IsGetter || method.IsSetter || method.IsAddOn || method.IsRemoveOn)
  					return true;
  				if (settings.AnonymousMethods && method.Name.StartsWith("<", StringComparison.Ordinal) && method.IsCompilerGenerated())
  					return true;
  			}
  			TypeDefinition type = member as TypeDefinition;
  			if (type != null && type.DeclaringType != null) {
  				if (settings.AnonymousMethods && type.Name.StartsWith("<>c__DisplayClass", StringComparison.Ordinal) && type.IsCompilerGenerated())
  					return true;
  				if (settings.YieldReturn && YieldReturnDecompiler.IsCompilerGeneratorEnumerator(type))
  					return true;
  			} else if (type != null && type.IsCompilerGenerated()) {
  				if (type.Name.StartsWith("<PrivateImplementationDetails>", StringComparison.Ordinal))
  					return true;
  				if (type.IsAnonymousType())
  					return true;
  			}
  			FieldDefinition field = member as FieldDefinition;
  			if (field != null && field.IsCompilerGenerated()) {
  				if (settings.AnonymousMethods && field.Name.StartsWith("CS$<>", StringComparison.Ordinal))
  					return true;
  				if (settings.AutomaticProperties && field.Name.StartsWith("<", StringComparison.Ordinal) && field.Name.EndsWith("BackingField", StringComparison.Ordinal))
  					return true;
  			}
  			// event-fields are not [CompilerGenerated]
  			if (field != null && settings.AutomaticEvents && field.DeclaringType.Events.Any(ev => ev.Name == field.Name))
  				return true;
  			return false;
  		}
  		
  		/// <summary>
  		/// Runs the C# transformations on the compilation unit.
  		/// </summary>
  		public void RunTransformations()
  		{
  			RunTransformations(null);
  		}
  		
  		public void RunTransformations(Predicate<IAstTransform> transformAbortCondition)
  		{
  			TransformationPipeline.RunTransformationsUntil(astCompileUnit, transformAbortCondition, context);
  			transformationsHaveRun = true;
  		}
  		
  		/// <summary>
  		/// Gets the abstract source tree.
  		/// </summary>
  		public CompilationUnit CompilationUnit {
  			get { return astCompileUnit; }
  		}
  		
  		/// <summary>
  		/// Generates C# code from the abstract source tree.
  		/// </summary>
  		/// <remarks>This method adds ParenthesizedExpressions into the AST, and will run transformations if <see cref="RunTransformations"/> was not called explicitly</remarks>
  		public void GenerateCode(ITextOutput output)
  		{
  			if (!transformationsHaveRun)
  				RunTransformations();
  			
  			astCompileUnit.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }, null);
  			var outputFormatter = new TextOutputFormatter(output);
  			var formattingPolicy = new CSharpFormattingOptions();
  			// disable whitespace in front of parentheses:
  			formattingPolicy.SpaceBeforeMethodCallParentheses = false;
  			formattingPolicy.SpaceBeforeMethodDeclarationParentheses = false;
  			formattingPolicy.SpaceBeforeConstructorDeclarationParentheses = false;
  			formattingPolicy.SpaceBeforeDelegateDeclarationParentheses = false;
  			astCompileUnit.AcceptVisitor(new OutputVisitor(outputFormatter, formattingPolicy), null);
  		}
  		
  		public void AddAssembly(AssemblyDefinition assemblyDefinition, bool onlyAssemblyLevel = false)
  		{
  			ConvertCustomAttributes(astCompileUnit, assemblyDefinition, "assembly");
  			ConvertCustomAttributes(astCompileUnit, assemblyDefinition.MainModule, "module");
  			
  			if (!onlyAssemblyLevel) {
  				foreach (TypeDefinition typeDef in assemblyDefinition.MainModule.Types) {
  					// Skip the <Module> class
  					if (typeDef.Name == "<Module>") continue;
  					// Skip any hidden types
  					if (AstBuilder.MemberIsHidden(typeDef, context.Settings))
  						continue;
  
  					AddType(typeDef);
  				}
  			}
  		}
  		
  		NamespaceDeclaration GetCodeNamespace(string name)
  		{
  			if (string.IsNullOrEmpty(name)) {
  				return null;
  			}
  			if (astNamespaces.ContainsKey(name)) {
  				return astNamespaces[name];
  			} else {
  				// Create the namespace
  				NamespaceDeclaration astNamespace = new NamespaceDeclaration { Name = name };
  				astCompileUnit.AddChild(astNamespace, CompilationUnit.MemberRole);
  				astNamespaces[name] = astNamespace;
  				return astNamespace;
  			}
  		}
  		
  		public void AddType(TypeDefinition typeDef)
  		{
  			var astType = CreateType(typeDef);
  			NamespaceDeclaration astNS = GetCodeNamespace(typeDef.Namespace);
  			if (astNS != null) {
  				astNS.AddChild(astType, NamespaceDeclaration.MemberRole);
  			} else {
  				astCompileUnit.AddChild(astType, CompilationUnit.MemberRole);
  			}
  		}
  		
  		public void AddMethod(MethodDefinition method)
  		{
  			AstNode node = method.IsConstructor ? (AstNode)CreateConstructor(method) : CreateMethod(method);
  			astCompileUnit.AddChild(node, CompilationUnit.MemberRole);
  		}
  
  		public void AddProperty(PropertyDefinition property)
  		{
  			astCompileUnit.AddChild(CreateProperty(property), CompilationUnit.MemberRole);
  		}
  		
  		public void AddField(FieldDefinition field)
  		{
  			astCompileUnit.AddChild(CreateField(field), CompilationUnit.MemberRole);
  		}
  		
  		public void AddEvent(EventDefinition ev)
  		{
  			astCompileUnit.AddChild(CreateEvent(ev), CompilationUnit.MemberRole);
  		}
  		
  		/// <summary>
  		/// Creates the AST for a type definition.
  		/// </summary>
  		/// <param name="typeDef"></param>
  		/// <returns>TypeDeclaration or DelegateDeclaration.</returns>
  		public AttributedNode CreateType(TypeDefinition typeDef)
  		{
  			// create CSharp code mappings - used for debugger
  			if (this.CodeMappings == null)
  				this.CodeMappings = new Tuple<string, List<MemberMapping>>(typeDef.FullName, new List<MemberMapping>());
  			
  			// create type
  			TypeDefinition oldCurrentType = context.CurrentType;
  			context.CurrentType = typeDef;
  			TypeDeclaration astType = new TypeDeclaration();
  			ConvertAttributes(astType, typeDef);
  			astType.AddAnnotation(typeDef);
  			astType.Modifiers = ConvertModifiers(typeDef);
  			astType.Name = CleanName(typeDef.Name);
  			
  			if (typeDef.IsEnum) {  // NB: Enum is value type
  				astType.ClassType = ClassType.Enum;
  				astType.Modifiers &= ~Modifiers.Sealed;
  			} else if (typeDef.IsValueType) {
  				astType.ClassType = ClassType.Struct;
  				astType.Modifiers &= ~Modifiers.Sealed;
  			} else if (typeDef.IsInterface) {
  				astType.ClassType = ClassType.Interface;
  				astType.Modifiers &= ~Modifiers.Abstract;
  			} else {
  				astType.ClassType = ClassType.Class;
  			}
  			
  			IEnumerable<GenericParameter> genericParameters = typeDef.GenericParameters;
  			if (typeDef.DeclaringType != null && typeDef.DeclaringType.HasGenericParameters)
  				genericParameters = genericParameters.Skip(typeDef.DeclaringType.GenericParameters.Count);
  			astType.TypeParameters.AddRange(MakeTypeParameters(genericParameters));
  			astType.Constraints.AddRange(MakeConstraints(genericParameters));
  			
  			// Nested types
  			foreach (TypeDefinition nestedTypeDef in typeDef.NestedTypes) {
  				if (MemberIsHidden(nestedTypeDef, context.Settings))
  					continue;
  				var nestedType = CreateType(nestedTypeDef);
  				SetNewModifier(nestedType);
  				astType.AddChild(nestedType, TypeDeclaration.MemberRole);
  			}
  			
  			AttributedNode result = astType;
  			if (typeDef.IsEnum) {
  				long expectedEnumMemberValue = 0;
  				bool forcePrintingInitializers = IsFlagsEnum(typeDef);
  				foreach (FieldDefinition field in typeDef.Fields) {
  					if (field.IsRuntimeSpecialName) {
  						// the value__ field
  						if (field.FieldType != typeDef.Module.TypeSystem.Int32) {
  							astType.AddChild(ConvertType(field.FieldType), TypeDeclaration.BaseTypeRole);
  						}
  					} else {
  						EnumMemberDeclaration enumMember = new EnumMemberDeclaration();
  						enumMember.Name = CleanName(field.Name);
  						long memberValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false);
  						if (forcePrintingInitializers || memberValue != expectedEnumMemberValue) {
  							enumMember.AddChild(new PrimitiveExpression(field.Constant), EnumMemberDeclaration.InitializerRole);
  						}
  						expectedEnumMemberValue = memberValue + 1;
  						astType.AddChild(enumMember, TypeDeclaration.MemberRole);
  					}
  				}
  			} else if (typeDef.BaseType != null && typeDef.BaseType.FullName == "System.MulticastDelegate") {
  				DelegateDeclaration dd = new DelegateDeclaration();
  				dd.Modifiers = astType.Modifiers & ~Modifiers.Sealed;
  				dd.Name = astType.Name;
  				dd.AddAnnotation(typeDef);
  				astType.Attributes.MoveTo(dd.Attributes);
  				astType.TypeParameters.MoveTo(dd.TypeParameters);
  				astType.Constraints.MoveTo(dd.Constraints);
  				foreach (var m in typeDef.Methods) {
  					if (m.Name == "Invoke") {
  						dd.ReturnType = ConvertType(m.ReturnType, m.MethodReturnType);
  						dd.Parameters.AddRange(MakeParameters(m));
  						ConvertAttributes(dd, m.MethodReturnType, m.Module);
  					}
  				}
  				result = dd;
  			} else {
  				// Base type
  				if (typeDef.BaseType != null && !typeDef.IsValueType && typeDef.BaseType.FullName != "System.Object") {
  					astType.AddChild(ConvertType(typeDef.BaseType), TypeDeclaration.BaseTypeRole);
  				}
  				foreach (var i in typeDef.Interfaces)
  					astType.AddChild(ConvertType(i), TypeDeclaration.BaseTypeRole);
  				
  				AddTypeMembers(astType, typeDef);
  
  				if (astType.Members.OfType<IndexerDeclaration>().Any(idx => idx.PrivateImplementationType.IsNull)) {
  					// Remove the [DefaultMember] attribute if the class contains indexers
  					foreach (AttributeSection section in astType.Attributes) {
  						foreach (Ast.Attribute attr in section.Attributes) {
  							TypeReference tr = attr.Type.Annotation<TypeReference>();
  							if (tr != null && tr.Name == "DefaultMemberAttribute" && tr.Namespace == "System.Reflection") {
  								attr.Remove();
  							}
  						}
  						if (section.Attributes.Count == 0)
  							section.Remove();
  					}
  				}
  			}
  
  			context.CurrentType = oldCurrentType;
  			return result;
  		}
  
  		internal static string CleanName(string name)
  		{
  			int pos = name.LastIndexOf('`');
  			if (pos >= 0)
  				name = name.Substring(0, pos);
  			pos = name.LastIndexOf('.');
  			if (pos >= 0)
  				name = name.Substring(pos + 1);
  			return name;
  		}
  
  		#region Convert Type Reference
  		/// <summary>
  		/// Converts a type reference.
  		/// </summary>
  		/// <param name="type">The Cecil type reference that should be converted into
  		/// a type system type reference.</param>
  		/// <param name="typeAttributes">Attributes associated with the Cecil type reference.
  		/// This is used to support the 'dynamic' type.</param>
  		public static AstType ConvertType(TypeReference type, ICustomAttributeProvider typeAttributes = null, ConvertTypeOptions options = ConvertTypeOptions.None)
  		{
  			int typeIndex = 0;
  			return ConvertType(type, typeAttributes, ref typeIndex, options);
  		}
  		
  		static AstType ConvertType(TypeReference type, ICustomAttributeProvider typeAttributes, ref int typeIndex, ConvertTypeOptions options)
  		{
  			while (type is OptionalModifierType || type is RequiredModifierType) {
  				type = ((TypeSpecification)type).ElementType;
  			}
  			if (type == null) {
  				return AstType.Null;
  			}
  			
  			if (type is Mono.Cecil.ByReferenceType) {
  				typeIndex++;
  				// by reference type cannot be represented in C#; so we'll represent it as a pointer instead
  				return ConvertType((type as Mono.Cecil.ByReferenceType).ElementType, typeAttributes, ref typeIndex, options)
  					.MakePointerType();
  			} else if (type is Mono.Cecil.PointerType) {
  				typeIndex++;
  				return ConvertType((type as Mono.Cecil.PointerType).ElementType, typeAttributes, ref typeIndex, options)
  					.MakePointerType();
  			} else if (type is Mono.Cecil.ArrayType) {
  				typeIndex++;
  				return ConvertType((type as Mono.Cecil.ArrayType).ElementType, typeAttributes, ref typeIndex, options)
  					.MakeArrayType((type as Mono.Cecil.ArrayType).Rank);
  			} else if (type is GenericInstanceType) {
  				GenericInstanceType gType = (GenericInstanceType)type;
  				if (gType.ElementType.Namespace == "System" && gType.ElementType.Name == "Nullable`1" && gType.GenericArguments.Count == 1) {
  					typeIndex++;
  					return new ComposedType {
  						BaseType = ConvertType(gType.GenericArguments[0], typeAttributes, ref typeIndex, options),
  						HasNullableSpecifier = true
  					};
  				}
  				AstType baseType = ConvertType(gType.ElementType, typeAttributes, ref typeIndex, options & ~ConvertTypeOptions.IncludeTypeParameterDefinitions);
  				List<AstType> typeArguments = new List<AstType>();
  				foreach (var typeArgument in gType.GenericArguments) {
  					typeIndex++;
  					typeArguments.Add(ConvertType(typeArgument, typeAttributes, ref typeIndex, options));
  				}
  				ApplyTypeArgumentsTo(baseType, typeArguments);
  				return baseType;
  			} else if (type is GenericParameter) {
  				return new SimpleType(type.Name);
  			} else if (type.IsNested) {
  				AstType typeRef = ConvertType(type.DeclaringType, typeAttributes, ref typeIndex, options & ~ConvertTypeOptions.IncludeTypeParameterDefinitions);
  				string namepart = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name);
  				MemberType memberType = new MemberType { Target = typeRef, MemberName = namepart };
  				memberType.AddAnnotation(type);
  				if ((options & ConvertTypeOptions.IncludeTypeParameterDefinitions) == ConvertTypeOptions.IncludeTypeParameterDefinitions) {
  					AddTypeParameterDefininitionsTo(type, memberType);
  				}
  				return memberType;
  			} else {
  				string ns = type.Namespace ?? string.Empty;
  				string name = type.Name;
  				if (name == null)
  					throw new InvalidOperationException("type.Name returned null. Type: " + type.ToString());
  				
  				if (name == "Object" && ns == "System" && HasDynamicAttribute(typeAttributes, typeIndex)) {
  					return new PrimitiveType("dynamic");
  				} else {
  					if (ns == "System") {
  						switch (name) {
  							case "SByte":
  								return new PrimitiveType("sbyte");
  							case "Int16":
  								return new PrimitiveType("short");
  							case "Int32":
  								return new PrimitiveType("int");
  							case "Int64":
  								return new PrimitiveType("long");
  							case "Byte":
  								return new PrimitiveType("byte");
  							case "UInt16":
  								return new PrimitiveType("ushort");
  							case "UInt32":
  								return new PrimitiveType("uint");
  							case "UInt64":
  								return new PrimitiveType("ulong");
  							case "String":
  								return new PrimitiveType("string");
  							case "Single":
  								return new PrimitiveType("float");
  							case "Double":
  								return new PrimitiveType("double");
  							case "Decimal":
  								return new PrimitiveType("decimal");
  							case "Char":
  								return new PrimitiveType("char");
  							case "Boolean":
  								return new PrimitiveType("bool");
  							case "Void":
  								return new PrimitiveType("void");
  							case "Object":
  								return new PrimitiveType("object");
  						}
  					}
  					
  					name = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(name);
  					
  					AstType astType;
  					if ((options & ConvertTypeOptions.IncludeNamespace) == ConvertTypeOptions.IncludeNamespace && ns.Length > 0) {
  						string[] parts = ns.Split('.');
  						AstType nsType = new SimpleType(parts[0]);
  						for (int i = 1; i < parts.Length; i++) {
  							nsType = new MemberType { Target = nsType, MemberName = parts[i] };
  						}
  						astType = new MemberType { Target = nsType, MemberName = name };
  					} else {
  						astType = new SimpleType(name);
  					}
  					astType.AddAnnotation(type);
  					
  					if ((options & ConvertTypeOptions.IncludeTypeParameterDefinitions) == ConvertTypeOptions.IncludeTypeParameterDefinitions) {
  						AddTypeParameterDefininitionsTo(type, astType);
  					}
  					return astType;
  				}
  			}
  		}
  		
  		static void AddTypeParameterDefininitionsTo(TypeReference type, AstType astType)
  		{
  			if (type.HasGenericParameters) {
  				List<AstType> typeArguments = new List<AstType>();
  				foreach (GenericParameter gp in type.GenericParameters) {
  					typeArguments.Add(new SimpleType(gp.Name));
  				}
  				ApplyTypeArgumentsTo(astType, typeArguments);
  			}
  		}
  		
  		static void ApplyTypeArgumentsTo(AstType baseType, List<AstType> typeArguments)
  		{
  			SimpleType st = baseType as SimpleType;
  			if (st != null) {
  				st.TypeArguments.AddRange(typeArguments);
  			}
  			MemberType mt = baseType as MemberType;
  			if (mt != null) {
  				TypeReference type = mt.Annotation<TypeReference>();
  				if (type != null) {
  					int typeParameterCount;
  					ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out typeParameterCount);
  					if (typeParameterCount > typeArguments.Count)
  						typeParameterCount = typeArguments.Count;
  					mt.TypeArguments.AddRange(typeArguments.GetRange(typeArguments.Count - typeParameterCount, typeParameterCount));
  					typeArguments.RemoveRange(typeArguments.Count - typeParameterCount, typeParameterCount);
  					if (typeArguments.Count > 0)
  						ApplyTypeArgumentsTo(mt.Target, typeArguments);
  				} else {
  					mt.TypeArguments.AddRange(typeArguments);
  				}
  			}
  		}
  		
  		const string DynamicAttributeFullName = "System.Runtime.CompilerServices.DynamicAttribute";
  		
  		static bool HasDynamicAttribute(ICustomAttributeProvider attributeProvider, int typeIndex)
  		{
  			if (attributeProvider == null || !attributeProvider.HasCustomAttributes)
  				return false;
  			foreach (CustomAttribute a in attributeProvider.CustomAttributes) {
  				if (a.Constructor.DeclaringType.FullName == DynamicAttributeFullName) {
  					if (a.ConstructorArguments.Count == 1) {
  						CustomAttributeArgument[] values = a.ConstructorArguments[0].Value as CustomAttributeArgument[];
  						if (values != null && typeIndex < values.Length && values[typeIndex].Value is bool)
  							return (bool)values[typeIndex].Value;
  					}
  					return true;
  				}
  			}
  			return false;
  		}
  		#endregion
  		
  		#region ConvertModifiers
  		Modifiers ConvertModifiers(TypeDefinition typeDef)
  		{
  			Modifiers modifiers = Modifiers.None;
  			if (typeDef.IsNestedPrivate)
  				modifiers |= Modifiers.Private;
  			else if (typeDef.IsNestedAssembly || typeDef.IsNestedFamilyAndAssembly || typeDef.IsNotPublic)
  				modifiers |= Modifiers.Internal;
  			else if (typeDef.IsNestedFamily)
  				modifiers |= Modifiers.Protected;
  			else if (typeDef.IsNestedFamilyOrAssembly)
  				modifiers |= Modifiers.Protected | Modifiers.Internal;
  			else if (typeDef.IsPublic || typeDef.IsNestedPublic)
  				modifiers |= Modifiers.Public;
  			
  			if (typeDef.IsAbstract && typeDef.IsSealed)
  				modifiers |= Modifiers.Static;
  			else if (typeDef.IsAbstract)
  				modifiers |= Modifiers.Abstract;
  			else if (typeDef.IsSealed)
  				modifiers |= Modifiers.Sealed;
  			
  			return modifiers;
  		}
  		
  		Modifiers ConvertModifiers(FieldDefinition fieldDef)
  		{
  			Modifiers modifiers = Modifiers.None;
  			if (fieldDef.IsPrivate)
  				modifiers |= Modifiers.Private;
  			else if (fieldDef.IsAssembly || fieldDef.IsFamilyAndAssembly)
  				modifiers |= Modifiers.Internal;
  			else if (fieldDef.IsFamily)
  				modifiers |= Modifiers.Protected;
  			else if (fieldDef.IsFamilyOrAssembly)
  				modifiers |= Modifiers.Protected | Modifiers.Internal;
  			else if (fieldDef.IsPublic)
  				modifiers |= Modifiers.Public;
  			
  			if (fieldDef.IsLiteral) {
  				modifiers |= Modifiers.Const;
  			} else {
  				if (fieldDef.IsStatic)
  					modifiers |= Modifiers.Static;
  				
  				if (fieldDef.IsInitOnly)
  					modifiers |= Modifiers.Readonly;
  			}
  			
  			return modifiers;
  		}
  		
  		Modifiers ConvertModifiers(MethodDefinition methodDef)
  		{
  			if (methodDef == null)
  				return Modifiers.None;
  			Modifiers modifiers = Modifiers.None;
  			if (methodDef.IsPrivate)
  				modifiers |= Modifiers.Private;
  			else if (methodDef.IsAssembly || methodDef.IsFamilyAndAssembly)
  				modifiers |= Modifiers.Internal;
  			else if (methodDef.IsFamily)
  				modifiers |= Modifiers.Protected;
  			else if (methodDef.IsFamilyOrAssembly)
  				modifiers |= Modifiers.Protected | Modifiers.Internal;
  			else if (methodDef.IsPublic)
  				modifiers |= Modifiers.Public;
  			
  			if (methodDef.IsStatic)
  				modifiers |= Modifiers.Static;
  			
  			if (methodDef.IsAbstract) {
  				modifiers |= Modifiers.Abstract;
  				if (!methodDef.IsNewSlot)
  					modifiers |= Modifiers.Override;
  			} else if (methodDef.IsFinal) {
  				if (!methodDef.IsNewSlot) {
  					modifiers |= Modifiers.Sealed | Modifiers.Override;
  				}
  			} else if (methodDef.IsVirtual) {
  				if (methodDef.IsNewSlot)
  					modifiers |= Modifiers.Virtual;
  				else
  					modifiers |= Modifiers.Override;
  			}
  			if (!methodDef.HasBody && !methodDef.IsAbstract)
  				modifiers |= Modifiers.Extern;
  			
  			return modifiers;
  		}
  
  		#endregion
  		
  		void AddTypeMembers(TypeDeclaration astType, TypeDefinition typeDef)
  		{
  			// Add fields
  			foreach(FieldDefinition fieldDef in typeDef.Fields) {
  				if (MemberIsHidden(fieldDef, context.Settings)) continue;
  				astType.AddChild(CreateField(fieldDef), TypeDeclaration.MemberRole);
  			}
  			
  			// Add events
  			foreach(EventDefinition eventDef in typeDef.Events) {
  				astType.AddChild(CreateEvent(eventDef), TypeDeclaration.MemberRole);
  			}
  
  			// Add properties
  			foreach(PropertyDefinition propDef in typeDef.Properties) {
  				astType.Members.Add(CreateProperty(propDef));
  			}
  			
  			// Add methods
  			foreach(MethodDefinition methodDef in typeDef.Methods) {
  				if (MemberIsHidden(methodDef, context.Settings)) continue;
  				
  				if (methodDef.IsConstructor)
  					astType.Members.Add(CreateConstructor(methodDef));
  				else
  					astType.Members.Add(CreateMethod(methodDef));
  			}
  		}
  
  		AttributedNode CreateMethod(MethodDefinition methodDef)
  		{
  			// Create mapping - used in debugger
  			MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings);
  			
  			MethodDeclaration astMethod = new MethodDeclaration().WithAnnotation(methodMapping);
  			astMethod.AddAnnotation(methodDef);
  			astMethod.ReturnType = ConvertType(methodDef.ReturnType, methodDef.MethodReturnType);
  			astMethod.Name = CleanName(methodDef.Name);
  			astMethod.TypeParameters.AddRange(MakeTypeParameters(methodDef.GenericParameters));
  			astMethod.Parameters.AddRange(MakeParameters(methodDef));
  			// constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly
  			if (!methodDef.IsVirtual || (methodDef.IsNewSlot && !methodDef.IsPrivate)) astMethod.Constraints.AddRange(MakeConstraints(methodDef.GenericParameters));
  			if (!methodDef.DeclaringType.IsInterface) {
  				if (!methodDef.HasOverrides) {
  					astMethod.Modifiers = ConvertModifiers(methodDef);
  					if (methodDef.IsVirtual == methodDef.IsNewSlot)
  						SetNewModifier(astMethod);
  				} else {
  					astMethod.PrivateImplementationType = ConvertType(methodDef.Overrides.First().DeclaringType);
  				}
  				astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters);
  			}
  			ConvertAttributes(astMethod, methodDef);
  			if (methodDef.HasCustomAttributes && astMethod.Parameters.Count > 0) {
  				foreach (CustomAttribute ca in methodDef.CustomAttributes) {
  					if (ca.AttributeType.Name == "ExtensionAttribute" && ca.AttributeType.Namespace == "System.Runtime.CompilerServices") {
  						astMethod.Parameters.First().ParameterModifier = ParameterModifier.This;
  					}
  				}
  			}
  			
  			// Convert MethodDeclaration to OperatorDeclaration if possible
  			if (methodDef.IsSpecialName && !methodDef.HasGenericParameters) {
  				OperatorType? opType = OperatorDeclaration.GetOperatorType(methodDef.Name);
  				if (opType.HasValue) {
  					OperatorDeclaration op = new OperatorDeclaration();
  					op.CopyAnnotationsFrom(astMethod);
  					op.ReturnType = astMethod.ReturnType.Detach();
  					op.OperatorType = opType.Value;
  					op.Modifiers = astMethod.Modifiers;
  					astMethod.Parameters.MoveTo(op.Parameters);
  					astMethod.Attributes.MoveTo(op.Attributes);
  					op.Body = astMethod.Body.Detach();
  					return op;
  				}
  			}
  			return astMethod;
  		}
  
  		IEnumerable<TypeParameterDeclaration> MakeTypeParameters(IEnumerable<GenericParameter> genericParameters)
  		{
  			foreach (var gp in genericParameters) {
  				TypeParameterDeclaration tp = new TypeParameterDeclaration();
  				tp.Name = CleanName(gp.Name);
  				if (gp.IsContravariant)
  					tp.Variance = VarianceModifier.Contravariant;
  				else if (gp.IsCovariant)
  					tp.Variance = VarianceModifier.Covariant;
  				ConvertCustomAttributes(tp, gp);
  				yield return tp;
  			}
  		}
  		
  		IEnumerable<Constraint> MakeConstraints(IEnumerable<GenericParameter> genericParameters)
  		{
  			foreach (var gp in genericParameters) {
  				Constraint c = new Constraint();
  				c.TypeParameter = CleanName(gp.Name);
  				// class/struct must be first
  				if (gp.HasReferenceTypeConstraint)
  					c.BaseTypes.Add(new PrimitiveType("class"));
  				if (gp.HasNotNullableValueTypeConstraint)
  					c.BaseTypes.Add(new PrimitiveType("struct"));
  				
  				foreach (var constraintType in gp.Constraints) {
  					if (gp.HasNotNullableValueTypeConstraint && constraintType.FullName == "System.ValueType")
  						continue;
  					c.BaseTypes.Add(ConvertType(constraintType));
  				}
  				
  				if (gp.HasDefaultConstructorConstraint && !gp.HasNotNullableValueTypeConstraint)
  					c.BaseTypes.Add(new PrimitiveType("new")); // new() must be last
  				if (c.BaseTypes.Any())
  					yield return c;
  			}
  		}
  		
  		ConstructorDeclaration CreateConstructor(MethodDefinition methodDef)
  		{
  			// Create mapping - used in debugger
  			MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings);
  			
  			ConstructorDeclaration astMethod = new ConstructorDeclaration();
  			astMethod.AddAnnotation(methodDef);
  			astMethod.Modifiers = ConvertModifiers(methodDef);
  			if (methodDef.IsStatic) {
  				// don't show visibility for static ctors
  				astMethod.Modifiers &= ~Modifiers.VisibilityMask;
  			}
  			astMethod.Name = CleanName(methodDef.DeclaringType.Name);
  			astMethod.Parameters.AddRange(MakeParameters(methodDef));
  			astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters);
  			ConvertAttributes(astMethod, methodDef);
  			astMethod.WithAnnotation(methodMapping);
  			return astMethod;
  		}
  
  		Modifiers FixUpVisibility(Modifiers m)
  		{
  			Modifiers v = m & Modifiers.VisibilityMask;
  			// If any of the modifiers is public, use that
  			if ((v & Modifiers.Public) == Modifiers.Public)
  				return Modifiers.Public | (m & ~Modifiers.VisibilityMask);
  			// If both modifiers are private, no need to fix anything
  
gt: 			if (v == Modifiers.Private)