mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1699 lines
68 KiB
1699 lines
68 KiB
// 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 = ICSharpCode.NRefactory.CSharp; |
|
using VarianceModifier = ICSharpCode.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; |
|
this.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.CoreLibrary)), |
|
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 (AstBuilder.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.CoreLibrary)), |
|
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(); |
|
ConvertCustomAttributes(enumMember, field); |
|
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 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") { |
|
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 = 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; |
|
} |
|
|
|
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)); |
|
bool createMethodBody = false; |
|
// 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); |
|
} |
|
createMethodBody = true; |
|
} else if (methodDef.IsStatic) { |
|
// decompile static method in interface |
|
astMethod.Modifiers = ConvertModifiers(methodDef); |
|
createMethodBody = true; |
|
} |
|
if (createMethodBody) { |
|
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.CoreLibrary); |
|
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.CoreLibrary); |
|
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.CoreLibrary); |
|
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.CoreLibrary)); |
|
} |
|
return attr; |
|
} |
|
|
|
static void ConvertCustomAttributes(AstNode attributedNode, ICustomAttributeProvider customAttributeProvider, string attributeTarget = null) |
|
{ |
|
EntityDeclaration entityDecl = attributedNode as EntityDeclaration; |
|
if (customAttributeProvider.HasCustomAttributes) { |
|
var attributes = new List<ICSharpCode.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 ICSharpCode.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<ICSharpCode.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 ICSharpCode.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.CoreLibrary); |
|
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); |
|
} |
|
} |
|
|
|
private 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 Ast.PrimitiveExpression(false); |
|
else if (TypeAnalysis.IsBoolean(type) && val == 1) |
|
return new Ast.PrimitiveExpression(true); |
|
else if (val == 0 && type is PointerType) |
|
return new Ast.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 && object.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 Ast.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 Ast.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. |
|
} |
|
} |
|
|
|
private 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)); |
|
} |
|
} |
|
}
|
|
|