diff options
Diffstat (limited to 'ICSharpCode.Decompiler/Ast/AstBuilder.cs')
-rw-r--r-- | ICSharpCode.Decompiler/Ast/AstBuilder.cs | 1690 |
1 files changed, 1690 insertions, 0 deletions
diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs new file mode 100644 index 00000000..faab2725 --- /dev/null +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -0,0 +1,1690 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +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 = NRefactory.CSharp; + using VarianceModifier = NRefactory.TypeSystem.VarianceModifier; + + [Flags] + public enum ConvertTypeOptions + { + None = 0, + IncludeNamespace = 1, + IncludeTypeParameterDefinitions = 2, + DoNotUsePrimitiveTypeNames = 4 + } + + public class AstBuilder + { + DecompilerContext context; + SyntaxTree syntaxTree = new SyntaxTree(); + Dictionary<string, NamespaceDeclaration> astNamespaces = new Dictionary<string, NamespaceDeclaration>(); + bool transformationsHaveRun; + + public AstBuilder(DecompilerContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + this.context = context; + DecompileMethodBodies = true; + } + + 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.HasGeneratedName() && method.IsCompilerGenerated()) + return true; + } + + TypeDefinition type = member as TypeDefinition; + if (type != null) { + if (type.DeclaringType != null) { + if (settings.AnonymousMethods && IsClosureType(type)) + return true; + if (settings.YieldReturn && YieldReturnDecompiler.IsCompilerGeneratorEnumerator(type)) + return true; + if (settings.AsyncAwait && AsyncDecompiler.IsCompilerGeneratedStateMachine(type)) + return true; + } else if (type.IsCompilerGenerated()) { + if (type.Name.StartsWith("<PrivateImplementationDetails>", StringComparison.Ordinal)) + return true; + if (type.IsAnonymousType()) + return true; + } + } + + FieldDefinition field = member as FieldDefinition; + if (field != null) { + if (field.IsCompilerGenerated()) { + if (settings.AnonymousMethods && IsAnonymousMethodCacheField(field)) + return true; + if (settings.AutomaticProperties && IsAutomaticPropertyBackingField(field)) + return true; + if (settings.SwitchStatementOnString && IsSwitchOnStringCache(field)) + return true; + } + // event-fields are not [CompilerGenerated] + if (settings.AutomaticEvents && field.DeclaringType.Events.Any(ev => ev.Name == field.Name)) + return true; + } + + return false; + } + + static bool IsSwitchOnStringCache(FieldDefinition field) + { + return field.Name.StartsWith("<>f__switch", StringComparison.Ordinal); + } + + static bool IsAutomaticPropertyBackingField(FieldDefinition field) + { + return field.HasGeneratedName() && field.Name.EndsWith("BackingField", StringComparison.Ordinal); + } + + static bool IsAnonymousMethodCacheField(FieldDefinition field) + { + return field.Name.StartsWith("CS$<>", StringComparison.Ordinal) || field.Name.StartsWith("<>f__am", StringComparison.Ordinal); + } + + static bool IsClosureType(TypeDefinition type) + { + return type.HasGeneratedName() && type.IsCompilerGenerated() && (type.Name.Contains("DisplayClass") || type.Name.Contains("AnonStorey")); + } + + /// <summary> + /// Runs the C# transformations on the compilation unit. + /// </summary> + public void RunTransformations() + { + RunTransformations(null); + } + + public void RunTransformations(Predicate<IAstTransform> transformAbortCondition) + { + TransformationPipeline.RunTransformationsUntil(syntaxTree, transformAbortCondition, context); + transformationsHaveRun = true; + } + + /// <summary> + /// Gets the abstract source tree. + /// </summary> + public SyntaxTree SyntaxTree { + get { return syntaxTree; } + } + + /// <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(); + + syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); + var outputFormatter = new TextTokenWriter(output, context) { FoldBraces = context.Settings.FoldBraces }; + var formattingPolicy = context.Settings.CSharpFormattingOptions; + syntaxTree.AcceptVisitor(new CSharpOutputVisitor(outputFormatter, formattingPolicy)); + } + + public void AddAssembly(AssemblyDefinition assemblyDefinition, bool onlyAssemblyLevel = false) + { + AddAssembly(assemblyDefinition.MainModule, onlyAssemblyLevel); + } + + public void AddAssembly(ModuleDefinition moduleDefinition, bool onlyAssemblyLevel = false) + { + if (moduleDefinition.Assembly != null && moduleDefinition.Assembly.Name.Version != null) { + syntaxTree.AddChild( + new AttributeSection { + AttributeTarget = "assembly", + Attributes = { + new NRefactory.CSharp.Attribute { + Type = new SimpleType("AssemblyVersion") + .WithAnnotation(new TypeReference( + "System.Reflection", "AssemblyVersionAttribute", + moduleDefinition, moduleDefinition.TypeSystem.Corlib)), + Arguments = { + new PrimitiveExpression(moduleDefinition.Assembly.Name.Version.ToString()) + } + } + } + }, EntityDeclaration.AttributeRole); + } + + if (moduleDefinition.Assembly != null) { + ConvertCustomAttributes(syntaxTree, moduleDefinition.Assembly, "assembly"); + ConvertSecurityAttributes(syntaxTree, moduleDefinition.Assembly, "assembly"); + } + ConvertCustomAttributes(syntaxTree, moduleDefinition, "module"); + AddTypeForwarderAttributes(syntaxTree, moduleDefinition, "assembly"); + + if (!onlyAssemblyLevel) { + foreach (TypeDefinition typeDef in moduleDefinition.Types) { + // Skip the <Module> class + if (typeDef.Name == "<Module>") continue; + // Skip any hidden types + if (MemberIsHidden(typeDef, context.Settings)) + continue; + + AddType(typeDef); + } + } + } + + void AddTypeForwarderAttributes(SyntaxTree astCompileUnit, ModuleDefinition module, string target) + { + if (!module.HasExportedTypes) + return; + foreach (ExportedType type in module.ExportedTypes) { + if (type.IsForwarder) { + var forwardedType = CreateTypeOfExpression(new TypeReference(type.Namespace, type.Name, module, type.Scope)); + astCompileUnit.AddChild( + new AttributeSection { + AttributeTarget = target, + Attributes = { + new NRefactory.CSharp.Attribute { + Type = new SimpleType("TypeForwardedTo") + .WithAnnotation(new TypeReference( + "System.Runtime.CompilerServices", "TypeForwardedToAttribute", + module, module.TypeSystem.Corlib)), + Arguments = { forwardedType } + } + } + }, EntityDeclaration.AttributeRole); + } + } + } + + 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 }; + syntaxTree.Members.Add(astNamespace); + astNamespaces[name] = astNamespace; + return astNamespace; + } + } + + public void AddType(TypeDefinition typeDef) + { + var astType = CreateType(typeDef); + NamespaceDeclaration astNS = GetCodeNamespace(typeDef.Namespace); + if (astNS != null) { + astNS.Members.Add(astType); + } else { + syntaxTree.Members.Add(astType); + } + } + + public void AddMethod(MethodDefinition method) + { + AstNode node = method.IsConstructor ? (AstNode)CreateConstructor(method) : CreateMethod(method); + syntaxTree.Members.Add(node); + } + + public void AddProperty(PropertyDefinition property) + { + syntaxTree.Members.Add(CreateProperty(property)); + } + + public void AddField(FieldDefinition field) + { + syntaxTree.Members.Add(CreateField(field)); + } + + public void AddEvent(EventDefinition ev) + { + syntaxTree.Members.Add(CreateEvent(ev)); + } + + /// <summary> + /// Creates the AST for a type definition. + /// </summary> + /// <param name="typeDef"></param> + /// <returns>TypeDeclaration or DelegateDeclaration.</returns> + public EntityDeclaration CreateType(TypeDefinition typeDef) + { + // 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)); + + EntityDeclaration result = astType; + if (typeDef.IsEnum) { + long expectedEnumMemberValue = 0; + bool forcePrintingInitializers = IsFlagsEnum(typeDef); + TypeCode baseType = TypeCode.Int32; + foreach (FieldDefinition field in typeDef.Fields) { + if (!field.IsStatic) { + // the value__ field + if (field.FieldType != typeDef.Module.TypeSystem.Int32) { + astType.AddChild(ConvertType(field.FieldType), Roles.BaseType); + baseType = TypeAnalysis.GetTypeCode(field.FieldType); + } + } else { + EnumMemberDeclaration enumMember = new EnumMemberDeclaration(); + enumMember.AddAnnotation(field); + enumMember.Name = CleanName(field.Name); + long memberValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false); + if (forcePrintingInitializers || memberValue != expectedEnumMemberValue) { + enumMember.AddChild(new PrimitiveExpression(CSharpPrimitiveCast.Cast(baseType, field.Constant, false)), EnumMemberDeclaration.InitializerRole); + } + expectedEnumMemberValue = memberValue + 1; + astType.AddChild(enumMember, Roles.TypeMemberRole); + } + } + } 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), Roles.BaseType); + } + foreach (var i in typeDef.Interfaces) + astType.AddChild(ConvertType(i), Roles.BaseType); + + 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 Create TypeOf Expression + /// <summary> + /// Creates a typeof-expression for the specified type. + /// </summary> + public static TypeOfExpression CreateTypeOfExpression(TypeReference type) + { + return new TypeOfExpression(AddEmptyTypeArgumentsForUnboundGenerics(ConvertType(type))); + } + + static AstType AddEmptyTypeArgumentsForUnboundGenerics(AstType type) + { + TypeReference typeRef = type.Annotation<TypeReference>(); + if (typeRef == null) + return type; + TypeDefinition typeDef = typeRef.Resolve(); // need to resolve to figure out the number of type parameters + if (typeDef == null || !typeDef.HasGenericParameters) + return type; + SimpleType sType = type as SimpleType; + MemberType mType = type as MemberType; + if (sType != null) { + while (typeDef.GenericParameters.Count > sType.TypeArguments.Count) { + sType.TypeArguments.Add(new SimpleType("")); + } + } + + if (mType != null) { + AddEmptyTypeArgumentsForUnboundGenerics(mType.Target); + + int outerTypeParamCount = typeDef.DeclaringType == null ? 0 : typeDef.DeclaringType.GenericParameters.Count; + + while (typeDef.GenericParameters.Count - outerTypeParamCount > mType.TypeArguments.Count) { + mType.TypeArguments.Add(new SimpleType("")); + } + } + + return type; + } + #endregion + + #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 ByReferenceType) { + typeIndex++; + // by reference type cannot be represented in C#; so we'll represent it as a pointer instead + return ConvertType((type as ByReferenceType).ElementType, typeAttributes, ref typeIndex, options) + .MakePointerType(); + } else if (type is PointerType) { + typeIndex++; + return ConvertType((type as PointerType).ElementType, typeAttributes, ref typeIndex, options) + .MakePointerType(); + } else if (type is ArrayType) { + typeIndex++; + return ConvertType((type as ArrayType).ElementType, typeAttributes, ref typeIndex, options) + .MakeArrayType((type as 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 = 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") { + if ((options & ConvertTypeOptions.DoNotUsePrimitiveTypeNames) + != ConvertTypeOptions.DoNotUsePrimitiveTypeNames) { + 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 = 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; + 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; + } + + RequiredModifierType modreq = fieldDef.FieldType as RequiredModifierType; + if (modreq != null && modreq.ModifierType.FullName == typeof(IsVolatile).FullName) + modifiers |= Modifiers.Volatile; + + 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) + { + // Nested types + foreach (TypeDefinition nestedTypeDef in typeDef.NestedTypes) { + if (MemberIsHidden(nestedTypeDef, context.Settings)) + continue; + var nestedType = CreateType(nestedTypeDef); + SetNewModifier(nestedType); + astType.AddChild(nestedType, Roles.TypeMemberRole); + } + + // Add fields + foreach(FieldDefinition fieldDef in typeDef.Fields) { + if (MemberIsHidden(fieldDef, context.Settings)) continue; + astType.AddChild(CreateField(fieldDef), Roles.TypeMemberRole); + } + + // Add events + foreach(EventDefinition eventDef in typeDef.Events) { + astType.AddChild(CreateEvent(eventDef), Roles.TypeMemberRole); + } + + // 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)); + } + } + + EntityDeclaration CreateMethod(MethodDefinition methodDef) + { + MethodDeclaration astMethod = new MethodDeclaration(); + 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 (IsExplicitInterfaceImplementation(methodDef)) { + astMethod.PrivateImplementationType = ConvertType(methodDef.Overrides.First().DeclaringType); + } else { + astMethod.Modifiers = ConvertModifiers(methodDef); + if (methodDef.IsVirtual == methodDef.IsNewSlot) + SetNewModifier(astMethod); + } + astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters); + if (context.CurrentMethodIsAsync) { + astMethod.Modifiers |= Modifiers.Async; + context.CurrentMethodIsAsync = false; + } + } + 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; + } + + bool IsExplicitInterfaceImplementation(MethodDefinition methodDef) + { + return methodDef.HasOverrides && methodDef.IsPrivate; + } + + 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 = new SimpleType(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) + { + 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); + if (methodDef.IsStatic && methodDef.DeclaringType.IsBeforeFieldInit && !astMethod.Body.IsNull) { + astMethod.Body.InsertChildAfter(null, new Comment(" Note: this type is marked as 'beforefieldinit'."), Roles.Comment); + } + 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 + if (v == Modifiers.Private) + return m; + // Otherwise, use the other modifiers (internal and/or protected) + return m & ~Modifiers.Private; + } + + EntityDeclaration CreateProperty(PropertyDefinition propDef) + { + PropertyDeclaration astProp = new PropertyDeclaration(); + astProp.AddAnnotation(propDef); + var accessor = propDef.GetMethod ?? propDef.SetMethod; + Modifiers getterModifiers = Modifiers.None; + Modifiers setterModifiers = Modifiers.None; + if (IsExplicitInterfaceImplementation(accessor)) { + astProp.PrivateImplementationType = ConvertType(accessor.Overrides.First().DeclaringType); + } else if (!propDef.DeclaringType.IsInterface) { + getterModifiers = ConvertModifiers(propDef.GetMethod); + setterModifiers = ConvertModifiers(propDef.SetMethod); + astProp.Modifiers = FixUpVisibility(getterModifiers | setterModifiers); + try { + if (accessor.IsVirtual && !accessor.IsNewSlot && (propDef.GetMethod == null || propDef.SetMethod == null)) { + foreach (var basePropDef in TypesHierarchyHelpers.FindBaseProperties(propDef)) { + if (basePropDef.GetMethod != null && basePropDef.SetMethod != null) { + var propVisibilityModifiers = ConvertModifiers(basePropDef.GetMethod) | ConvertModifiers(basePropDef.SetMethod); + astProp.Modifiers = FixUpVisibility((astProp.Modifiers & ~Modifiers.VisibilityMask) | (propVisibilityModifiers & Modifiers.VisibilityMask)); + break; + } else if ((basePropDef.GetMethod ?? basePropDef.SetMethod).IsNewSlot) { + break; + } + } + } + } catch (ReferenceResolvingException) { + // TODO: add some kind of notification (a comment?) about possible problems with decompiled code due to unresolved references. + } + } + astProp.Name = CleanName(propDef.Name); + astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); + + if (propDef.GetMethod != null) { + astProp.Getter = new Accessor(); + astProp.Getter.Body = CreateMethodBody(propDef.GetMethod); + astProp.Getter.AddAnnotation(propDef.GetMethod); + ConvertAttributes(astProp.Getter, propDef.GetMethod); + + if ((getterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) + astProp.Getter.Modifiers = getterModifiers & Modifiers.VisibilityMask; + } + if (propDef.SetMethod != null) { + astProp.Setter = new Accessor(); + astProp.Setter.Body = CreateMethodBody(propDef.SetMethod); + astProp.Setter.AddAnnotation(propDef.SetMethod); + ConvertAttributes(astProp.Setter, propDef.SetMethod); + ParameterDefinition lastParam = propDef.SetMethod.Parameters.LastOrDefault(); + if (lastParam != null) { + ConvertCustomAttributes(astProp.Setter, lastParam, "param"); + if (lastParam.HasMarshalInfo) { + astProp.Setter.Attributes.Add(new AttributeSection(ConvertMarshalInfo(lastParam, propDef.Module)) { AttributeTarget = "param" }); + } + } + + if ((setterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) + astProp.Setter.Modifiers = setterModifiers & Modifiers.VisibilityMask; + } + ConvertCustomAttributes(astProp, propDef); + + EntityDeclaration member = astProp; + if(propDef.IsIndexer()) + member = ConvertPropertyToIndexer(astProp, propDef); + if(!accessor.HasOverrides && !accessor.DeclaringType.IsInterface) + if (accessor.IsVirtual == accessor.IsNewSlot) + SetNewModifier(member); + return member; + } + + IndexerDeclaration ConvertPropertyToIndexer(PropertyDeclaration astProp, PropertyDefinition propDef) + { + var astIndexer = new IndexerDeclaration(); + astIndexer.CopyAnnotationsFrom(astProp); + astProp.Attributes.MoveTo(astIndexer.Attributes); + astIndexer.Modifiers = astProp.Modifiers; + astIndexer.PrivateImplementationType = astProp.PrivateImplementationType.Detach(); + astIndexer.ReturnType = astProp.ReturnType.Detach(); + astIndexer.Getter = astProp.Getter.Detach(); + astIndexer.Setter = astProp.Setter.Detach(); + astIndexer.Parameters.AddRange(MakeParameters(propDef.Parameters)); + return astIndexer; + } + + EntityDeclaration CreateEvent(EventDefinition eventDef) + { + if (eventDef.AddMethod != null && eventDef.AddMethod.IsAbstract) { + // An abstract event cannot be custom + EventDeclaration astEvent = new EventDeclaration(); + ConvertCustomAttributes(astEvent, eventDef); + astEvent.AddAnnotation(eventDef); + astEvent.Variables.Add(new VariableInitializer(CleanName(eventDef.Name))); + astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef); + if (!eventDef.DeclaringType.IsInterface) + astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); + return astEvent; + } else { + CustomEventDeclaration astEvent = new CustomEventDeclaration(); + ConvertCustomAttributes(astEvent, eventDef); + astEvent.AddAnnotation(eventDef); + astEvent.Name = CleanName(eventDef.Name); + astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef); + if (eventDef.AddMethod == null || !IsExplicitInterfaceImplementation(eventDef.AddMethod)) + astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); + else + astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType); + + if (eventDef.AddMethod != null) { + astEvent.AddAccessor = new Accessor { + Body = CreateMethodBody(eventDef.AddMethod) + }.WithAnnotation(eventDef.AddMethod); + ConvertAttributes(astEvent.AddAccessor, eventDef.AddMethod); + } + if (eventDef.RemoveMethod != null) { + astEvent.RemoveAccessor = new Accessor { + Body = CreateMethodBody(eventDef.RemoveMethod) + }.WithAnnotation(eventDef.RemoveMethod); + ConvertAttributes(astEvent.RemoveAccessor, eventDef.RemoveMethod); + } + MethodDefinition accessor = eventDef.AddMethod ?? eventDef.RemoveMethod; + if (accessor.IsVirtual == accessor.IsNewSlot) { + SetNewModifier(astEvent); + } + return astEvent; + } + } + + public bool DecompileMethodBodies { get; set; } + + BlockStatement CreateMethodBody(MethodDefinition method, IEnumerable<ParameterDeclaration> parameters = null) + { + if (DecompileMethodBodies) + return AstMethodBodyBuilder.CreateMethodBody(method, context, parameters); + else + return null; + } + + FieldDeclaration CreateField(FieldDefinition fieldDef) + { + FieldDeclaration astField = new FieldDeclaration(); + astField.AddAnnotation(fieldDef); + VariableInitializer initializer = new VariableInitializer(CleanName(fieldDef.Name)); + astField.AddChild(initializer, Roles.Variable); + astField.ReturnType = ConvertType(fieldDef.FieldType, fieldDef); + astField.Modifiers = ConvertModifiers(fieldDef); + if (fieldDef.HasConstant) { + initializer.Initializer = CreateExpressionForConstant(fieldDef.Constant, fieldDef.FieldType, fieldDef.DeclaringType.IsEnum); + } + ConvertAttributes(astField, fieldDef); + SetNewModifier(astField); + return astField; + } + + static Expression CreateExpressionForConstant(object constant, TypeReference type, bool isEnumMemberDeclaration = false) + { + if (constant == null) { + if (type.IsValueType && !(type.Namespace == "System" && type.Name == "Nullable`1")) + return new DefaultValueExpression(ConvertType(type)); + else + return new NullReferenceExpression(); + } else { + TypeCode c = Type.GetTypeCode(constant.GetType()); + if (c >= TypeCode.SByte && c <= TypeCode.UInt64 && !isEnumMemberDeclaration) { + return MakePrimitive((long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constant, false), type); + } else { + return new PrimitiveExpression(constant); + } + } + } + + public static IEnumerable<ParameterDeclaration> MakeParameters(MethodDefinition method, bool isLambda = false) + { + var parameters = MakeParameters(method.Parameters, isLambda); + if (method.CallingConvention == MethodCallingConvention.VarArg) { + return parameters.Concat(new[] { new ParameterDeclaration { Type = new PrimitiveType("__arglist") } }); + } else { + return parameters; + } + } + + public static IEnumerable<ParameterDeclaration> MakeParameters(IEnumerable<ParameterDefinition> paramCol, bool isLambda = false) + { + foreach(ParameterDefinition paramDef in paramCol) { + ParameterDeclaration astParam = new ParameterDeclaration(); + astParam.AddAnnotation(paramDef); + if (!(isLambda && paramDef.ParameterType.ContainsAnonymousType())) + astParam.Type = ConvertType(paramDef.ParameterType, paramDef); + astParam.Name = paramDef.Name; + + if (paramDef.ParameterType is ByReferenceType) { + astParam.ParameterModifier = (!paramDef.IsIn && paramDef.IsOut) ? ParameterModifier.Out : ParameterModifier.Ref; + ComposedType ct = astParam.Type as ComposedType; + if (ct != null && ct.PointerRank > 0) + ct.PointerRank--; + } + + if (paramDef.HasCustomAttributes) { + foreach (CustomAttribute ca in paramDef.CustomAttributes) { + if (ca.AttributeType.Name == "ParamArrayAttribute" && ca.AttributeType.Namespace == "System") + astParam.ParameterModifier = ParameterModifier.Params; + } + } + if (paramDef.IsOptional) { + astParam.DefaultExpression = CreateExpressionForConstant(paramDef.Constant, paramDef.ParameterType); + } + + ConvertCustomAttributes(astParam, paramDef); + ModuleDefinition module = ((MethodDefinition)paramDef.Method).Module; + if (paramDef.HasMarshalInfo) { + astParam.Attributes.Add(new AttributeSection(ConvertMarshalInfo(paramDef, module))); + } + if (astParam.ParameterModifier != ParameterModifier.Out) { + if (paramDef.IsIn) + astParam.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(InAttribute), module))); + if (paramDef.IsOut) + astParam.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(OutAttribute), module))); + } + yield return astParam; + } + } + + #region ConvertAttributes + void ConvertAttributes(EntityDeclaration attributedNode, TypeDefinition typeDefinition) + { + ConvertCustomAttributes(attributedNode, typeDefinition); + ConvertSecurityAttributes(attributedNode, typeDefinition); + + // Handle the non-custom attributes: + #region SerializableAttribute + if (typeDefinition.IsSerializable) + attributedNode.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(SerializableAttribute)))); + #endregion + + #region ComImportAttribute + if (typeDefinition.IsImport) + attributedNode.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(ComImportAttribute)))); + #endregion + + #region StructLayoutAttribute + LayoutKind layoutKind = LayoutKind.Auto; + switch (typeDefinition.Attributes & TypeAttributes.LayoutMask) { + case TypeAttributes.SequentialLayout: + layoutKind = LayoutKind.Sequential; + break; + case TypeAttributes.ExplicitLayout: + layoutKind = LayoutKind.Explicit; + break; + } + CharSet charSet = CharSet.None; + switch (typeDefinition.Attributes & TypeAttributes.StringFormatMask) { + case TypeAttributes.AnsiClass: + charSet = CharSet.Ansi; + break; + case TypeAttributes.AutoClass: + charSet = CharSet.Auto; + break; + case TypeAttributes.UnicodeClass: + charSet = CharSet.Unicode; + break; + } + LayoutKind defaultLayoutKind = (typeDefinition.IsValueType && !typeDefinition.IsEnum) ? LayoutKind.Sequential: LayoutKind.Auto; + if (layoutKind != defaultLayoutKind || charSet != CharSet.Ansi || typeDefinition.PackingSize > 0 || typeDefinition.ClassSize > 0) { + var structLayout = CreateNonCustomAttribute(typeof(StructLayoutAttribute)); + structLayout.Arguments.Add(new IdentifierExpression("LayoutKind").Member(layoutKind.ToString())); + if (charSet != CharSet.Ansi) { + structLayout.AddNamedArgument("CharSet", new IdentifierExpression("CharSet").Member(charSet.ToString())); + } + if (typeDefinition.PackingSize > 0) { + structLayout.AddNamedArgument("Pack", new PrimitiveExpression((int)typeDefinition.PackingSize)); + } + if (typeDefinition.ClassSize > 0) { + structLayout.AddNamedArgument("Size", new PrimitiveExpression((int)typeDefinition.ClassSize)); + } + attributedNode.Attributes.Add(new AttributeSection(structLayout)); + } + #endregion + } + + void ConvertAttributes(EntityDeclaration attributedNode, MethodDefinition methodDefinition) + { + ConvertCustomAttributes(attributedNode, methodDefinition); + ConvertSecurityAttributes(attributedNode, methodDefinition); + + MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask; + + #region DllImportAttribute + if (methodDefinition.HasPInvokeInfo && methodDefinition.PInvokeInfo != null) { + PInvokeInfo info = methodDefinition.PInvokeInfo; + Ast.Attribute dllImport = CreateNonCustomAttribute(typeof(DllImportAttribute)); + dllImport.Arguments.Add(new PrimitiveExpression(info.Module.Name)); + + if (info.IsBestFitDisabled) + dllImport.AddNamedArgument("BestFitMapping", new PrimitiveExpression(false)); + if (info.IsBestFitEnabled) + dllImport.AddNamedArgument("BestFitMapping", new PrimitiveExpression(true)); + + CallingConvention callingConvention; + switch (info.Attributes & PInvokeAttributes.CallConvMask) { + case PInvokeAttributes.CallConvCdecl: + callingConvention = CallingConvention.Cdecl; + break; + case PInvokeAttributes.CallConvFastcall: + callingConvention = CallingConvention.FastCall; + break; + case PInvokeAttributes.CallConvStdCall: + callingConvention = CallingConvention.StdCall; + break; + case PInvokeAttributes.CallConvThiscall: + callingConvention = CallingConvention.ThisCall; + break; + case PInvokeAttributes.CallConvWinapi: + callingConvention = CallingConvention.Winapi; + break; + default: + throw new NotSupportedException("unknown calling convention"); + } + if (callingConvention != CallingConvention.Winapi) + dllImport.AddNamedArgument("CallingConvention", new IdentifierExpression("CallingConvention").Member(callingConvention.ToString())); + + CharSet charSet = CharSet.None; + switch (info.Attributes & PInvokeAttributes.CharSetMask) { + case PInvokeAttributes.CharSetAnsi: + charSet = CharSet.Ansi; + break; + case PInvokeAttributes.CharSetAuto: + charSet = CharSet.Auto; + break; + case PInvokeAttributes.CharSetUnicode: + charSet = CharSet.Unicode; + break; + } + if (charSet != CharSet.None) + dllImport.AddNamedArgument("CharSet", new IdentifierExpression("CharSet").Member(charSet.ToString())); + + if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != methodDefinition.Name) + dllImport.AddNamedArgument("EntryPoint", new PrimitiveExpression(info.EntryPoint)); + + if (info.IsNoMangle) + dllImport.AddNamedArgument("ExactSpelling", new PrimitiveExpression(true)); + + if ((implAttributes & MethodImplAttributes.PreserveSig) == MethodImplAttributes.PreserveSig) + implAttributes &= ~MethodImplAttributes.PreserveSig; + else + dllImport.AddNamedArgument("PreserveSig", new PrimitiveExpression(false)); + + if (info.SupportsLastError) + dllImport.AddNamedArgument("SetLastError", new PrimitiveExpression(true)); + + if (info.IsThrowOnUnmappableCharDisabled) + dllImport.AddNamedArgument("ThrowOnUnmappableChar", new PrimitiveExpression(false)); + if (info.IsThrowOnUnmappableCharEnabled) + dllImport.AddNamedArgument("ThrowOnUnmappableChar", new PrimitiveExpression(true)); + + attributedNode.Attributes.Add(new AttributeSection(dllImport)); + } + #endregion + + #region PreserveSigAttribute + if (implAttributes == MethodImplAttributes.PreserveSig) { + attributedNode.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(PreserveSigAttribute)))); + implAttributes = 0; + } + #endregion + + #region MethodImplAttribute + if (implAttributes != 0) { + Ast.Attribute methodImpl = CreateNonCustomAttribute(typeof(MethodImplAttribute)); + TypeReference methodImplOptions = new TypeReference( + "System.Runtime.CompilerServices", "MethodImplOptions", + methodDefinition.Module, methodDefinition.Module.TypeSystem.Corlib); + methodImpl.Arguments.Add(MakePrimitive((long)implAttributes, methodImplOptions)); + attributedNode.Attributes.Add(new AttributeSection(methodImpl)); + } + #endregion + + ConvertAttributes(attributedNode, methodDefinition.MethodReturnType, methodDefinition.Module); + } + + void ConvertAttributes(EntityDeclaration attributedNode, MethodReturnType methodReturnType, ModuleDefinition module) + { + ConvertCustomAttributes(attributedNode, methodReturnType, "return"); + if (methodReturnType.HasMarshalInfo) { + var marshalInfo = ConvertMarshalInfo(methodReturnType, module); + attributedNode.Attributes.Add(new AttributeSection(marshalInfo) { AttributeTarget = "return" }); + } + } + + internal static void ConvertAttributes(EntityDeclaration attributedNode, FieldDefinition fieldDefinition, string attributeTarget = null) + { + ConvertCustomAttributes(attributedNode, fieldDefinition); + + #region FieldOffsetAttribute + if (fieldDefinition.HasLayoutInfo) { + Ast.Attribute fieldOffset = CreateNonCustomAttribute(typeof(FieldOffsetAttribute), fieldDefinition.Module); + fieldOffset.Arguments.Add(new PrimitiveExpression(fieldDefinition.Offset)); + attributedNode.Attributes.Add(new AttributeSection(fieldOffset) { AttributeTarget = attributeTarget }); + } + #endregion + + #region NonSerializedAttribute + if (fieldDefinition.IsNotSerialized) { + Ast.Attribute nonSerialized = CreateNonCustomAttribute(typeof(NonSerializedAttribute), fieldDefinition.Module); + attributedNode.Attributes.Add(new AttributeSection(nonSerialized) { AttributeTarget = attributeTarget }); + } + #endregion + + if (fieldDefinition.HasMarshalInfo) { + attributedNode.Attributes.Add(new AttributeSection(ConvertMarshalInfo(fieldDefinition, fieldDefinition.Module)) { AttributeTarget = attributeTarget }); + } + } + + #region MarshalAsAttribute (ConvertMarshalInfo) + static Ast.Attribute ConvertMarshalInfo(IMarshalInfoProvider marshalInfoProvider, ModuleDefinition module) + { + MarshalInfo marshalInfo = marshalInfoProvider.MarshalInfo; + Ast.Attribute attr = CreateNonCustomAttribute(typeof(MarshalAsAttribute), module); + var unmanagedType = new TypeReference("System.Runtime.InteropServices", "UnmanagedType", module, module.TypeSystem.Corlib); + attr.Arguments.Add(MakePrimitive((int)marshalInfo.NativeType, unmanagedType)); + + FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo; + if (fami != null) { + attr.AddNamedArgument("SizeConst", new PrimitiveExpression(fami.Size)); + if (fami.ElementType != NativeType.None) + attr.AddNamedArgument("ArraySubType", MakePrimitive((int)fami.ElementType, unmanagedType)); + } + SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo; + if (sami != null && sami.ElementType != VariantType.None) { + var varEnum = new TypeReference("System.Runtime.InteropServices", "VarEnum", module, module.TypeSystem.Corlib); + attr.AddNamedArgument("SafeArraySubType", MakePrimitive((int)sami.ElementType, varEnum)); + } + ArrayMarshalInfo ami = marshalInfo as ArrayMarshalInfo; + if (ami != null) { + if (ami.ElementType != NativeType.Max) + attr.AddNamedArgument("ArraySubType", MakePrimitive((int)ami.ElementType, unmanagedType)); + if (ami.Size >= 0) + attr.AddNamedArgument("SizeConst", new PrimitiveExpression(ami.Size)); + if (ami.SizeParameterMultiplier != 0 && ami.SizeParameterIndex >= 0) + attr.AddNamedArgument("SizeParamIndex", new PrimitiveExpression(ami.SizeParameterIndex)); + } + CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo; + if (cmi != null) { + attr.AddNamedArgument("MarshalType", new PrimitiveExpression(cmi.ManagedType.FullName)); + if (!string.IsNullOrEmpty(cmi.Cookie)) + attr.AddNamedArgument("MarshalCookie", new PrimitiveExpression(cmi.Cookie)); + } + FixedSysStringMarshalInfo fssmi = marshalInfo as FixedSysStringMarshalInfo; + if (fssmi != null) { + attr.AddNamedArgument("SizeConst", new PrimitiveExpression(fssmi.Size)); + } + return attr; + } + #endregion + + Ast.Attribute CreateNonCustomAttribute(Type attributeType) + { + return CreateNonCustomAttribute(attributeType, context.CurrentType != null ? context.CurrentType.Module : null); + } + + static Ast.Attribute CreateNonCustomAttribute(Type attributeType, ModuleDefinition module) + { + Debug.Assert(attributeType.Name.EndsWith("Attribute", StringComparison.Ordinal)); + Ast.Attribute attr = new Ast.Attribute(); + attr.Type = new SimpleType(attributeType.Name.Substring(0, attributeType.Name.Length - "Attribute".Length)); + if (module != null) { + attr.Type.AddAnnotation(new TypeReference(attributeType.Namespace, attributeType.Name, module, module.TypeSystem.Corlib)); + } + return attr; + } + + static void ConvertCustomAttributes(AstNode attributedNode, ICustomAttributeProvider customAttributeProvider, string attributeTarget = null) + { + EntityDeclaration entityDecl = attributedNode as EntityDeclaration; + if (customAttributeProvider.HasCustomAttributes) { + var attributes = new List<NRefactory.CSharp.Attribute>(); + foreach (var customAttribute in customAttributeProvider.CustomAttributes.OrderBy(a => a.AttributeType.FullName)) { + if (customAttribute.AttributeType.Name == "ExtensionAttribute" && customAttribute.AttributeType.Namespace == "System.Runtime.CompilerServices") { + // don't show the ExtensionAttribute (it's converted to the 'this' modifier) + continue; + } + if (customAttribute.AttributeType.Name == "ParamArrayAttribute" && customAttribute.AttributeType.Namespace == "System") { + // don't show the ParamArrayAttribute (it's converted to the 'params' modifier) + continue; + } + // if the method is async, remove [DebuggerStepThrough] and [Async + if (entityDecl != null && entityDecl.HasModifier(Modifiers.Async)) { + if (customAttribute.AttributeType.Name == "DebuggerStepThroughAttribute" && customAttribute.AttributeType.Namespace == "System.Diagnostics") { + continue; + } + if (customAttribute.AttributeType.Name == "AsyncStateMachineAttribute" && customAttribute.AttributeType.Namespace == "System.Runtime.CompilerServices") { + continue; + } + } + + var attribute = new NRefactory.CSharp.Attribute(); + attribute.AddAnnotation(customAttribute); + attribute.Type = ConvertType(customAttribute.AttributeType); + attributes.Add(attribute); + + SimpleType st = attribute.Type as SimpleType; + if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) { + st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - "Attribute".Length); + } + + if(customAttribute.HasConstructorArguments) { + foreach (var parameter in customAttribute.ConstructorArguments) { + Expression parameterValue = ConvertArgumentValue(parameter); + attribute.Arguments.Add(parameterValue); + } + } + if (customAttribute.HasProperties) { + TypeDefinition resolvedAttributeType = customAttribute.AttributeType.Resolve(); + foreach (var propertyNamedArg in customAttribute.Properties) { + var propertyReference = resolvedAttributeType != null ? resolvedAttributeType.Properties.FirstOrDefault(pr => pr.Name == propertyNamedArg.Name) : null; + var propertyName = new IdentifierExpression(propertyNamedArg.Name).WithAnnotation(propertyReference); + var argumentValue = ConvertArgumentValue(propertyNamedArg.Argument); + attribute.Arguments.Add(new AssignmentExpression(propertyName, argumentValue)); + } + } + + if (customAttribute.HasFields) { + TypeDefinition resolvedAttributeType = customAttribute.AttributeType.Resolve(); + foreach (var fieldNamedArg in customAttribute.Fields) { + var fieldReference = resolvedAttributeType != null ? resolvedAttributeType.Fields.FirstOrDefault(f => f.Name == fieldNamedArg.Name) : null; + var fieldName = new IdentifierExpression(fieldNamedArg.Name).WithAnnotation(fieldReference); + var argumentValue = ConvertArgumentValue(fieldNamedArg.Argument); + attribute.Arguments.Add(new AssignmentExpression(fieldName, argumentValue)); + } + } + } + + if (attributeTarget == "module" || attributeTarget == "assembly") { + // use separate section for each attribute + foreach (var attribute in attributes) { + var section = new AttributeSection(); + section.AttributeTarget = attributeTarget; + section.Attributes.Add(attribute); + attributedNode.AddChild(section, EntityDeclaration.AttributeRole); + } + } else if (attributes.Count > 0) { + // use single section for all attributes + var section = new AttributeSection(); + section.AttributeTarget = attributeTarget; + section.Attributes.AddRange(attributes); + attributedNode.AddChild(section, EntityDeclaration.AttributeRole); + } + } + } + + static void ConvertSecurityAttributes(AstNode attributedNode, ISecurityDeclarationProvider secDeclProvider, string attributeTarget = null) + { + if (!secDeclProvider.HasSecurityDeclarations) + return; + var attributes = new List<NRefactory.CSharp.Attribute>(); + foreach (var secDecl in secDeclProvider.SecurityDeclarations.OrderBy(d => d.Action)) { + foreach (var secAttribute in secDecl.SecurityAttributes.OrderBy(a => a.AttributeType.FullName)) { + var attribute = new NRefactory.CSharp.Attribute(); + attribute.AddAnnotation(secAttribute); + attribute.Type = ConvertType(secAttribute.AttributeType); + attributes.Add(attribute); + + SimpleType st = attribute.Type as SimpleType; + if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) { + st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - "Attribute".Length); + } + + var module = secAttribute.AttributeType.Module; + var securityActionType = new TypeReference("System.Security.Permissions", "SecurityAction", module, module.TypeSystem.Corlib); + attribute.Arguments.Add(MakePrimitive((int)secDecl.Action, securityActionType)); + + if (secAttribute.HasProperties) { + TypeDefinition resolvedAttributeType = secAttribute.AttributeType.Resolve(); + foreach (var propertyNamedArg in secAttribute.Properties) { + var propertyReference = resolvedAttributeType != null ? resolvedAttributeType.Properties.FirstOrDefault(pr => pr.Name == propertyNamedArg.Name) : null; + var propertyName = new IdentifierExpression(propertyNamedArg.Name).WithAnnotation(propertyReference); + var argumentValue = ConvertArgumentValue(propertyNamedArg.Argument); + attribute.Arguments.Add(new AssignmentExpression(propertyName, argumentValue)); + } + } + + if (secAttribute.HasFields) { + TypeDefinition resolvedAttributeType = secAttribute.AttributeType.Resolve(); + foreach (var fieldNamedArg in secAttribute.Fields) { + var fieldReference = resolvedAttributeType != null ? resolvedAttributeType.Fields.FirstOrDefault(f => f.Name == fieldNamedArg.Name) : null; + var fieldName = new IdentifierExpression(fieldNamedArg.Name).WithAnnotation(fieldReference); + var argumentValue = ConvertArgumentValue(fieldNamedArg.Argument); + attribute.Arguments.Add(new AssignmentExpression(fieldName, argumentValue)); + } + } + } + } + if (attributeTarget == "module" || attributeTarget == "assembly") { + // use separate section for each attribute + foreach (var attribute in attributes) { + var section = new AttributeSection(); + section.AttributeTarget = attributeTarget; + section.Attributes.Add(attribute); + attributedNode.AddChild(section, EntityDeclaration.AttributeRole); + } + } else if (attributes.Count > 0) { + // use single section for all attributes + var section = new AttributeSection(); + section.AttributeTarget = attributeTarget; + section.Attributes.AddRange(attributes); + attributedNode.AddChild(section, EntityDeclaration.AttributeRole); + } + } + + static Expression ConvertArgumentValue(CustomAttributeArgument argument) + { + if (argument.Value is CustomAttributeArgument[]) { + ArrayInitializerExpression arrayInit = new ArrayInitializerExpression(); + foreach (CustomAttributeArgument element in (CustomAttributeArgument[])argument.Value) { + arrayInit.Elements.Add(ConvertArgumentValue(element)); + } + ArrayType arrayType = argument.Type as ArrayType; + return new ArrayCreateExpression { + Type = ConvertType(arrayType != null ? arrayType.ElementType : argument.Type), + AdditionalArraySpecifiers = { new ArraySpecifier() }, + Initializer = arrayInit + }; + } else if (argument.Value is CustomAttributeArgument) { + // occurs with boxed arguments + return ConvertArgumentValue((CustomAttributeArgument)argument.Value); + } + var type = argument.Type.Resolve(); + if (type != null && type.IsEnum) { + return MakePrimitive(Convert.ToInt64(argument.Value), type); + } else if (argument.Value is TypeReference) { + return CreateTypeOfExpression((TypeReference)argument.Value); + } else { + return new PrimitiveExpression(argument.Value); + } + } + #endregion + + internal static Expression MakePrimitive(long val, TypeReference type) + { + if (TypeAnalysis.IsBoolean(type) && val == 0) + return new PrimitiveExpression(false); + else if (TypeAnalysis.IsBoolean(type) && val == 1) + return new PrimitiveExpression(true); + else if (val == 0 && type is PointerType) + return new NullReferenceExpression(); + if (type != null) + { // cannot rely on type.IsValueType, it's not set for typerefs (but is set for typespecs) + TypeDefinition enumDefinition = type.Resolve(); + if (enumDefinition != null && enumDefinition.IsEnum) { + TypeCode enumBaseTypeCode = TypeCode.Int32; + foreach (FieldDefinition field in enumDefinition.Fields) { + if (field.IsStatic && Equals(CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false), val)) + return ConvertType(type).Member(field.Name).WithAnnotation(field); + else if (!field.IsStatic) + enumBaseTypeCode = TypeAnalysis.GetTypeCode(field.FieldType); // use primitive type of the enum + } + if (IsFlagsEnum(enumDefinition)) { + long enumValue = val; + Expression expr = null; + long negatedEnumValue = ~val; + // limit negatedEnumValue to the appropriate range + switch (enumBaseTypeCode) { + case TypeCode.Byte: + case TypeCode.SByte: + negatedEnumValue &= byte.MaxValue; + break; + case TypeCode.Int16: + case TypeCode.UInt16: + negatedEnumValue &= ushort.MaxValue; + break; + case TypeCode.Int32: + case TypeCode.UInt32: + negatedEnumValue &= uint.MaxValue; + break; + } + Expression negatedExpr = null; + foreach (FieldDefinition field in enumDefinition.Fields.Where(fld => fld.IsStatic)) { + long fieldValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false); + if (fieldValue == 0) + continue; // skip None enum value + + if ((fieldValue & enumValue) == fieldValue) { + var fieldExpression = ConvertType(type).Member(field.Name).WithAnnotation(field); + if (expr == null) + expr = fieldExpression; + else + expr = new BinaryOperatorExpression(expr, BinaryOperatorType.BitwiseOr, fieldExpression); + + enumValue &= ~fieldValue; + } + if ((fieldValue & negatedEnumValue) == fieldValue) { + var fieldExpression = ConvertType(type).Member(field.Name).WithAnnotation(field); + if (negatedExpr == null) + negatedExpr = fieldExpression; + else + negatedExpr = new BinaryOperatorExpression(negatedExpr, BinaryOperatorType.BitwiseOr, fieldExpression); + + negatedEnumValue &= ~fieldValue; + } + } + if (enumValue == 0 && expr != null) { + if (!(negatedEnumValue == 0 && negatedExpr != null && negatedExpr.Descendants.Count() < expr.Descendants.Count())) { + return expr; + } + } + if (negatedEnumValue == 0 && negatedExpr != null) { + return new UnaryOperatorExpression(UnaryOperatorType.BitNot, negatedExpr); + } + } + return new PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false)).CastTo(ConvertType(type)); + } + } + TypeCode code = TypeAnalysis.GetTypeCode(type); + if (code == TypeCode.Object || code == TypeCode.Empty) + code = TypeCode.Int32; + return new PrimitiveExpression(CSharpPrimitiveCast.Cast(code, val, false)); + } + + static bool IsFlagsEnum(TypeDefinition type) + { + if (!type.HasCustomAttributes) + return false; + + return type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.FlagsAttribute"); + } + + /// <summary> + /// Sets new modifier if the member hides some other member from a base type. + /// </summary> + /// <param name="member">The node of the member which new modifier state should be determined.</param> + static void SetNewModifier(EntityDeclaration member) + { + try { + bool addNewModifier = false; + if (member is IndexerDeclaration) { + var propertyDef = member.Annotation<PropertyDefinition>(); + var baseProperties = + TypesHierarchyHelpers.FindBaseProperties(propertyDef); + addNewModifier = baseProperties.Any(); + } else + addNewModifier = HidesBaseMember(member); + + if (addNewModifier) + member.Modifiers |= Modifiers.New; + } + catch (ReferenceResolvingException) { + // TODO: add some kind of notification (a comment?) about possible problems with decompiled code due to unresolved references. + } + } + + static bool HidesBaseMember(EntityDeclaration member) + { + var memberDefinition = member.Annotation<IMemberDefinition>(); + bool addNewModifier = false; + var methodDefinition = memberDefinition as MethodDefinition; + if (methodDefinition != null) { + addNewModifier = HidesByName(memberDefinition, includeBaseMethods: false); + if (!addNewModifier) + addNewModifier = TypesHierarchyHelpers.FindBaseMethods(methodDefinition).Any(); + } else + addNewModifier = HidesByName(memberDefinition, includeBaseMethods: true); + return addNewModifier; + } + + /// <summary> + /// Determines whether any base class member has the same name as the given member. + /// </summary> + /// <param name="member">The derived type's member.</param> + /// <param name="includeBaseMethods">true if names of methods declared in base types should also be checked.</param> + /// <returns>true if any base member has the same name as given member, otherwise false.</returns> + static bool HidesByName(IMemberDefinition member, bool includeBaseMethods) + { + Debug.Assert(!(member is PropertyDefinition) || !((PropertyDefinition)member).IsIndexer()); + + if (member.DeclaringType.BaseType != null) { + var baseTypeRef = member.DeclaringType.BaseType; + while (baseTypeRef != null) { + var baseType = baseTypeRef.ResolveOrThrow(); + if (baseType.HasProperties && AnyIsHiddenBy(baseType.Properties, member, m => !m.IsIndexer())) + return true; + if (baseType.HasEvents && AnyIsHiddenBy(baseType.Events, member)) + return true; + if (baseType.HasFields && AnyIsHiddenBy(baseType.Fields, member)) + return true; + if (includeBaseMethods && baseType.HasMethods + && AnyIsHiddenBy(baseType.Methods, member, m => !m.IsSpecialName)) + return true; + if (baseType.HasNestedTypes && AnyIsHiddenBy(baseType.NestedTypes, member)) + return true; + baseTypeRef = baseType.BaseType; + } + } + return false; + } + + static bool AnyIsHiddenBy<T>(IEnumerable<T> members, IMemberDefinition derived, Predicate<T> condition = null) + where T : IMemberDefinition + { + return members.Any(m => m.Name == derived.Name + && (condition == null || condition(m)) + && TypesHierarchyHelpers.IsVisibleFromDerived(m, derived.DeclaringType)); + } + } +} |