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.
1018 lines
38 KiB
1018 lines
38 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Diagnostics; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Runtime.CompilerServices; |
|
using System.Runtime.InteropServices; |
|
using System.Threading; |
|
using ICSharpCode.Decompiler; |
|
using ICSharpCode.Decompiler.Ast.Transforms; |
|
using ICSharpCode.Decompiler.ILAst; |
|
using ICSharpCode.NRefactory.CSharp; |
|
using ICSharpCode.NRefactory.Utils; |
|
using Mono.Cecil; |
|
using Mono.Cecil.Cil; |
|
|
|
namespace ICSharpCode.Decompiler.Ast |
|
{ |
|
using Ast = ICSharpCode.NRefactory.CSharp; |
|
using ClassType = ICSharpCode.NRefactory.TypeSystem.ClassType; |
|
using VarianceModifier = ICSharpCode.NRefactory.TypeSystem.VarianceModifier; |
|
|
|
public class AstBuilder |
|
{ |
|
DecompilerContext context = new DecompilerContext(); |
|
CompilationUnit astCompileUnit = new CompilationUnit(); |
|
Dictionary<string, NamespaceDeclaration> astNamespaces = new Dictionary<string, NamespaceDeclaration>(); |
|
|
|
public AstBuilder(DecompilerContext context) |
|
{ |
|
if (context == null) |
|
throw new ArgumentNullException("context"); |
|
this.context = context; |
|
} |
|
|
|
public static bool MemberIsHidden(MemberReference member) |
|
{ |
|
MethodDefinition method = member as MethodDefinition; |
|
if (method != null && (method.IsGetter || method.IsSetter || method.IsAddOn || method.IsRemoveOn)) |
|
return true; |
|
if (method != null && method.Name.StartsWith("<", StringComparison.Ordinal) && method.IsCompilerGenerated()) |
|
return true; |
|
TypeDefinition type = member as TypeDefinition; |
|
if (type != null && type.DeclaringType != null && type.Name.StartsWith("<>c__DisplayClass", StringComparison.Ordinal) && type.IsCompilerGenerated()) |
|
return true; |
|
FieldDefinition field = member as FieldDefinition; |
|
if (field != null && field.Name.StartsWith("CS$<>", StringComparison.Ordinal) && field.IsCompilerGenerated()) |
|
return true; |
|
return false; |
|
} |
|
|
|
public void GenerateCode(ITextOutput output) |
|
{ |
|
GenerateCode(output, null); |
|
} |
|
|
|
public void GenerateCode(ITextOutput output, Predicate<IAstTransform> transformAbortCondition) |
|
{ |
|
TransformationPipeline.RunTransformationsUntil(astCompileUnit, transformAbortCondition, context); |
|
astCompileUnit.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }, null); |
|
|
|
var outputFormatter = new TextOutputFormatter(output); |
|
var formattingPolicy = new CSharpFormattingPolicy(); |
|
// disable whitespace in front of parentheses: |
|
formattingPolicy.BeforeMethodCallParentheses = false; |
|
formattingPolicy.BeforeMethodDeclarationParentheses = false; |
|
formattingPolicy.BeforeConstructorDeclarationParentheses = false; |
|
formattingPolicy.BeforeDelegateDeclarationParentheses = false; |
|
astCompileUnit.AcceptVisitor(new OutputVisitor(outputFormatter, formattingPolicy), null); |
|
} |
|
|
|
public void AddAssembly(AssemblyDefinition assemblyDefinition, bool onlyAssemblyLevel = false) |
|
{ |
|
astCompileUnit.AddChild( |
|
new UsingDeclaration { |
|
Import = new SimpleType("System") |
|
}, CompilationUnit.MemberRole); |
|
|
|
ConvertCustomAttributes(astCompileUnit, assemblyDefinition, AttributeTarget.Assembly); |
|
ConvertCustomAttributes(astCompileUnit, assemblyDefinition.MainModule, AttributeTarget.Module); |
|
|
|
if (!onlyAssemblyLevel) { |
|
foreach (TypeDefinition typeDef in assemblyDefinition.MainModule.Types) |
|
{ |
|
// Skip nested types - they will be added by the parent type |
|
if (typeDef.DeclaringType != null) continue; |
|
// Skip the <Module> class |
|
if (typeDef.Name == "<Module>") continue; |
|
|
|
AddType(typeDef); |
|
} |
|
} |
|
} |
|
|
|
NamespaceDeclaration GetCodeNamespace(string name) |
|
{ |
|
if (string.IsNullOrEmpty(name)) { |
|
return null; |
|
} |
|
if (astNamespaces.ContainsKey(name)) { |
|
return astNamespaces[name]; |
|
} else { |
|
// Create the namespace |
|
NamespaceDeclaration astNamespace = new NamespaceDeclaration { Name = name }; |
|
astCompileUnit.AddChild(astNamespace, CompilationUnit.MemberRole); |
|
astNamespaces[name] = astNamespace; |
|
return astNamespace; |
|
} |
|
} |
|
|
|
public void AddType(TypeDefinition typeDef) |
|
{ |
|
TypeDeclaration astType = CreateType(typeDef); |
|
NamespaceDeclaration astNS = GetCodeNamespace(typeDef.Namespace); |
|
if (astNS != null) { |
|
astNS.AddChild(astType, NamespaceDeclaration.MemberRole); |
|
} else { |
|
astCompileUnit.AddChild(astType, CompilationUnit.MemberRole); |
|
} |
|
} |
|
|
|
public void AddMethod(MethodDefinition method) |
|
{ |
|
AstNode node = method.IsConstructor ? (AstNode)CreateConstructor(method) : CreateMethod(method); |
|
astCompileUnit.AddChild(node, CompilationUnit.MemberRole); |
|
} |
|
|
|
public void AddProperty(PropertyDefinition property) |
|
{ |
|
astCompileUnit.AddChild(CreateProperty(property), CompilationUnit.MemberRole); |
|
} |
|
|
|
public void AddField(FieldDefinition field) |
|
{ |
|
astCompileUnit.AddChild(CreateField(field), CompilationUnit.MemberRole); |
|
} |
|
|
|
public void AddEvent(EventDefinition ev) |
|
{ |
|
astCompileUnit.AddChild(CreateEvent(ev), CompilationUnit.MemberRole); |
|
} |
|
|
|
public TypeDeclaration CreateType(TypeDefinition typeDef) |
|
{ |
|
TypeDeclaration astType = new TypeDeclaration(); |
|
astType.AddAnnotation(typeDef); |
|
astType.Modifiers = ConvertModifiers(typeDef); |
|
astType.Name = CleanName(typeDef.Name); |
|
|
|
if (typeDef.IsEnum) { // NB: Enum is value type |
|
astType.ClassType = ClassType.Enum; |
|
astType.Modifiers &= ~Modifiers.Sealed; |
|
} else if (typeDef.IsValueType) { |
|
astType.ClassType = ClassType.Struct; |
|
astType.Modifiers &= ~Modifiers.Sealed; |
|
} else if (typeDef.IsInterface) { |
|
astType.ClassType = ClassType.Interface; |
|
astType.Modifiers &= ~Modifiers.Abstract; |
|
} else { |
|
astType.ClassType = ClassType.Class; |
|
} |
|
|
|
IEnumerable<GenericParameter> genericParameters = typeDef.GenericParameters; |
|
if (typeDef.DeclaringType != null && typeDef.DeclaringType.HasGenericParameters) |
|
genericParameters = genericParameters.Skip(typeDef.DeclaringType.GenericParameters.Count); |
|
astType.TypeParameters.AddRange(MakeTypeParameters(genericParameters)); |
|
astType.Constraints.AddRange(MakeConstraints(genericParameters)); |
|
|
|
// Nested types |
|
foreach(TypeDefinition nestedTypeDef in typeDef.NestedTypes) { |
|
if (MemberIsHidden(nestedTypeDef)) |
|
continue; |
|
astType.AddChild(CreateType(nestedTypeDef), TypeDeclaration.MemberRole); |
|
} |
|
|
|
|
|
if (typeDef.IsEnum) { |
|
long expectedEnumMemberValue = 0; |
|
bool forcePrintingInitializers = IsFlagsEnum(typeDef); |
|
foreach (FieldDefinition field in typeDef.Fields) { |
|
if (field.IsRuntimeSpecialName) { |
|
// the value__ field |
|
if (field.FieldType != typeDef.Module.TypeSystem.Int32) { |
|
astType.AddChild(ConvertType(field.FieldType), TypeDeclaration.BaseTypeRole); |
|
} |
|
} else { |
|
EnumMemberDeclaration enumMember = new EnumMemberDeclaration(); |
|
enumMember.Name = CleanName(field.Name); |
|
long memberValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false); |
|
if (forcePrintingInitializers || memberValue != expectedEnumMemberValue) { |
|
enumMember.AddChild(new PrimitiveExpression(field.Constant), EnumMemberDeclaration.InitializerRole); |
|
} |
|
expectedEnumMemberValue = memberValue + 1; |
|
astType.AddChild(enumMember, TypeDeclaration.MemberRole); |
|
} |
|
} |
|
} else { |
|
// Base type |
|
if (typeDef.BaseType != null && !typeDef.IsValueType && typeDef.BaseType.FullName != "System.Object") { |
|
astType.AddChild(ConvertType(typeDef.BaseType), TypeDeclaration.BaseTypeRole); |
|
} |
|
foreach (var i in typeDef.Interfaces) |
|
astType.AddChild(ConvertType(i), TypeDeclaration.BaseTypeRole); |
|
|
|
|
|
AddTypeMembers(astType, typeDef); |
|
} |
|
|
|
ConvertAttributes(astType, typeDef); |
|
return astType; |
|
} |
|
|
|
public void Transform(IAstTransform transform) |
|
{ |
|
transform.Run(astCompileUnit); |
|
} |
|
|
|
string CleanName(string name) |
|
{ |
|
int pos = name.LastIndexOf('`'); |
|
if (pos >= 0) |
|
name = name.Substring(0, pos); |
|
return name; |
|
} |
|
|
|
#region Convert Type Reference |
|
/// <summary> |
|
/// Converts a type reference. |
|
/// </summary> |
|
/// <param name="type">The Cecil type reference that should be converted into |
|
/// a type system type reference.</param> |
|
/// <param name="typeAttributes">Attributes associated with the Cecil type reference. |
|
/// This is used to support the 'dynamic' type.</param> |
|
public static AstType ConvertType(TypeReference type, ICustomAttributeProvider typeAttributes = null) |
|
{ |
|
int typeIndex = 0; |
|
return ConvertType(type, typeAttributes, ref typeIndex); |
|
} |
|
|
|
static AstType ConvertType(TypeReference type, ICustomAttributeProvider typeAttributes, ref int typeIndex) |
|
{ |
|
while (type is OptionalModifierType || type is RequiredModifierType) { |
|
type = ((TypeSpecification)type).ElementType; |
|
} |
|
if (type == null) { |
|
return AstType.Null; |
|
} |
|
|
|
if (type is Mono.Cecil.ByReferenceType) { |
|
typeIndex++; |
|
// ignore by reference type (cannot be represented in C#) |
|
return ConvertType((type as Mono.Cecil.ByReferenceType).ElementType, typeAttributes, ref typeIndex); |
|
} else if (type is Mono.Cecil.PointerType) { |
|
typeIndex++; |
|
return ConvertType((type as Mono.Cecil.PointerType).ElementType, typeAttributes, ref typeIndex) |
|
.MakePointerType(); |
|
} else if (type is Mono.Cecil.ArrayType) { |
|
typeIndex++; |
|
return ConvertType((type as Mono.Cecil.ArrayType).ElementType, typeAttributes, ref typeIndex) |
|
.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), |
|
HasNullableSpecifier = true |
|
}; |
|
} |
|
AstType baseType = ConvertType(gType.ElementType, typeAttributes, ref typeIndex); |
|
List<AstType> typeArguments = new List<AstType>(); |
|
foreach (var typeArgument in gType.GenericArguments) { |
|
typeIndex++; |
|
typeArguments.Add(ConvertType(typeArgument, typeAttributes, ref typeIndex)); |
|
} |
|
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); |
|
string namepart = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name); |
|
return new MemberType { Target = typeRef, MemberName = namepart }.WithAnnotation(type); |
|
} else { |
|
string ns = type.Namespace ?? string.Empty; |
|
string name = type.Name; |
|
if (name == null) |
|
throw new InvalidOperationException("type.Name returned null. Type: " + type.ToString()); |
|
|
|
if (name == "Object" && ns == "System" && HasDynamicAttribute(typeAttributes, typeIndex)) { |
|
return new PrimitiveType("dynamic"); |
|
} else { |
|
if (ns == "System") { |
|
switch (name) { |
|
case "SByte": |
|
return new PrimitiveType("sbyte"); |
|
case "Int16": |
|
return new PrimitiveType("short"); |
|
case "Int32": |
|
return new PrimitiveType("int"); |
|
case "Int64": |
|
return new PrimitiveType("long"); |
|
case "Byte": |
|
return new PrimitiveType("byte"); |
|
case "UInt16": |
|
return new PrimitiveType("ushort"); |
|
case "UInt32": |
|
return new PrimitiveType("uint"); |
|
case "UInt64": |
|
return new PrimitiveType("ulong"); |
|
case "String": |
|
return new PrimitiveType("string"); |
|
case "Single": |
|
return new PrimitiveType("float"); |
|
case "Double": |
|
return new PrimitiveType("double"); |
|
case "Decimal": |
|
return new PrimitiveType("decimal"); |
|
case "Char": |
|
return new PrimitiveType("char"); |
|
case "Boolean": |
|
return new PrimitiveType("bool"); |
|
case "Void": |
|
return new PrimitiveType("void"); |
|
case "Object": |
|
return new PrimitiveType("object"); |
|
} |
|
} |
|
|
|
name = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(name); |
|
|
|
// TODO: Until we can simplify type with 'using', use just the name without namesapce |
|
return new SimpleType(name).WithAnnotation(type); |
|
|
|
// if (ns.Length == 0) |
|
// return new SimpleType(name).WithAnnotation(type); |
|
// 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] }; |
|
// } |
|
// return new MemberType { Target = nsType, MemberName = name }.WithAnnotation(type); |
|
} |
|
} |
|
} |
|
|
|
static void ApplyTypeArgumentsTo(AstType baseType, List<AstType> typeArguments) |
|
{ |
|
SimpleType st = baseType as SimpleType; |
|
if (st != null) { |
|
st.TypeArguments.AddRange(typeArguments); |
|
} |
|
MemberType mt = baseType as MemberType; |
|
if (mt != null) { |
|
TypeReference type = mt.Annotation<TypeReference>(); |
|
if (type != null) { |
|
int typeParameterCount; |
|
ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out typeParameterCount); |
|
if (typeParameterCount > typeArguments.Count) |
|
typeParameterCount = typeArguments.Count; |
|
mt.TypeArguments.AddRange(typeArguments.GetRange(typeArguments.Count - typeParameterCount, typeParameterCount)); |
|
typeArguments.RemoveRange(typeArguments.Count - typeParameterCount, typeParameterCount); |
|
if (typeArguments.Count > 0) |
|
ApplyTypeArgumentsTo(mt.Target, typeArguments); |
|
} else { |
|
mt.TypeArguments.AddRange(typeArguments); |
|
} |
|
} |
|
} |
|
|
|
const string DynamicAttributeFullName = "System.Runtime.CompilerServices.DynamicAttribute"; |
|
|
|
static bool HasDynamicAttribute(ICustomAttributeProvider attributeProvider, int typeIndex) |
|
{ |
|
if (attributeProvider == null || !attributeProvider.HasCustomAttributes) |
|
return false; |
|
foreach (CustomAttribute a in attributeProvider.CustomAttributes) { |
|
if (a.Constructor.DeclaringType.FullName == DynamicAttributeFullName) { |
|
if (a.ConstructorArguments.Count == 1) { |
|
CustomAttributeArgument[] values = a.ConstructorArguments[0].Value as CustomAttributeArgument[]; |
|
if (values != null && typeIndex < values.Length && values[typeIndex].Value is bool) |
|
return (bool)values[typeIndex].Value; |
|
} |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
#endregion |
|
|
|
#region ConvertModifiers |
|
Modifiers ConvertModifiers(TypeDefinition typeDef) |
|
{ |
|
Modifiers modifiers = Modifiers.None; |
|
if (typeDef.IsNestedPrivate) |
|
modifiers |= Modifiers.Private; |
|
else if (typeDef.IsNestedAssembly || typeDef.IsNestedFamilyAndAssembly || typeDef.IsNotPublic) |
|
modifiers |= Modifiers.Internal; |
|
else if (typeDef.IsNestedFamily) |
|
modifiers |= Modifiers.Protected; |
|
else if (typeDef.IsNestedFamilyOrAssembly) |
|
modifiers |= Modifiers.Protected | Modifiers.Internal; |
|
else if (typeDef.IsPublic || typeDef.IsNestedPublic) |
|
modifiers |= Modifiers.Public; |
|
|
|
if (typeDef.IsAbstract && typeDef.IsSealed) |
|
modifiers |= Modifiers.Static; |
|
else if (typeDef.IsAbstract) |
|
modifiers |= Modifiers.Abstract; |
|
else if (typeDef.IsSealed) |
|
modifiers |= Modifiers.Sealed; |
|
|
|
return modifiers; |
|
} |
|
|
|
Modifiers ConvertModifiers(FieldDefinition fieldDef) |
|
{ |
|
Modifiers modifiers = Modifiers.None; |
|
if (fieldDef.IsPrivate) |
|
modifiers |= Modifiers.Private; |
|
else if (fieldDef.IsAssembly || fieldDef.IsFamilyAndAssembly) |
|
modifiers |= Modifiers.Internal; |
|
else if (fieldDef.IsFamily) |
|
modifiers |= Modifiers.Protected; |
|
else if (fieldDef.IsFamilyOrAssembly) |
|
modifiers |= Modifiers.Protected | Modifiers.Internal; |
|
else if (fieldDef.IsPublic) |
|
modifiers |= Modifiers.Public; |
|
|
|
if (fieldDef.IsLiteral) { |
|
modifiers |= Modifiers.Const; |
|
} else { |
|
if (fieldDef.IsStatic) |
|
modifiers |= Modifiers.Static; |
|
|
|
if (fieldDef.IsInitOnly) |
|
modifiers |= Modifiers.Readonly; |
|
} |
|
|
|
return modifiers; |
|
} |
|
|
|
Modifiers ConvertModifiers(MethodDefinition methodDef) |
|
{ |
|
if (methodDef == null) |
|
return Modifiers.None; |
|
Modifiers modifiers = Modifiers.None; |
|
if (methodDef.IsPrivate) |
|
modifiers |= Modifiers.Private; |
|
else if (methodDef.IsAssembly || methodDef.IsFamilyAndAssembly) |
|
modifiers |= Modifiers.Internal; |
|
else if (methodDef.IsFamily) |
|
modifiers |= Modifiers.Protected; |
|
else if (methodDef.IsFamilyOrAssembly) |
|
modifiers |= Modifiers.Protected | Modifiers.Internal; |
|
else if (methodDef.IsPublic) |
|
modifiers |= Modifiers.Public; |
|
|
|
if (methodDef.IsStatic) |
|
modifiers |= Modifiers.Static; |
|
|
|
if (methodDef.IsAbstract) { |
|
modifiers |= Modifiers.Abstract; |
|
if (!methodDef.IsNewSlot) |
|
modifiers |= Modifiers.Override; |
|
} else if (methodDef.IsFinal) { |
|
if (!methodDef.IsNewSlot) { |
|
modifiers |= Modifiers.Sealed | Modifiers.Override; |
|
} |
|
} else if (methodDef.IsVirtual) { |
|
if (methodDef.IsNewSlot) |
|
modifiers |= Modifiers.Virtual; |
|
else |
|
modifiers |= Modifiers.Override; |
|
} |
|
if (!methodDef.HasBody && !methodDef.IsAbstract) |
|
modifiers |= Modifiers.Extern; |
|
|
|
return modifiers; |
|
} |
|
#endregion |
|
|
|
void AddTypeMembers(TypeDeclaration astType, TypeDefinition typeDef) |
|
{ |
|
// Add fields |
|
foreach(FieldDefinition fieldDef in typeDef.Fields) { |
|
if (MemberIsHidden(fieldDef)) continue; |
|
astType.AddChild(CreateField(fieldDef), TypeDeclaration.MemberRole); |
|
} |
|
|
|
// Add events |
|
foreach(EventDefinition eventDef in typeDef.Events) { |
|
astType.AddChild(CreateEvent(eventDef), TypeDeclaration.MemberRole); |
|
} |
|
|
|
// Add properties |
|
foreach(PropertyDefinition propDef in typeDef.Properties) { |
|
astType.AddChild(CreateProperty(propDef), TypeDeclaration.MemberRole); |
|
} |
|
|
|
// Add constructors |
|
foreach(MethodDefinition methodDef in typeDef.Methods) { |
|
if (!methodDef.IsConstructor) continue; |
|
|
|
astType.AddChild(CreateConstructor(methodDef), TypeDeclaration.MemberRole); |
|
} |
|
|
|
// Add methods |
|
foreach(MethodDefinition methodDef in typeDef.Methods) { |
|
if (methodDef.IsConstructor || MemberIsHidden(methodDef)) continue; |
|
|
|
astType.AddChild(CreateMethod(methodDef), TypeDeclaration.MemberRole); |
|
} |
|
} |
|
|
|
MethodDeclaration 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.Parameters)); |
|
astMethod.Constraints.AddRange(MakeConstraints(methodDef.GenericParameters)); |
|
if (!methodDef.DeclaringType.IsInterface) { |
|
astMethod.Modifiers = ConvertModifiers(methodDef); |
|
astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, context); |
|
} |
|
ConvertAttributes(astMethod, methodDef); |
|
return astMethod; |
|
} |
|
|
|
IEnumerable<TypeParameterDeclaration> MakeTypeParameters(IEnumerable<GenericParameter> genericParameters) |
|
{ |
|
foreach (var gp in genericParameters) { |
|
TypeParameterDeclaration tp = new TypeParameterDeclaration(); |
|
tp.Name = CleanName(gp.Name); |
|
if (gp.IsContravariant) |
|
tp.Variance = VarianceModifier.Contravariant; |
|
else if (gp.IsCovariant) |
|
tp.Variance = VarianceModifier.Covariant; |
|
ConvertCustomAttributes(tp, gp); |
|
yield return tp; |
|
} |
|
} |
|
|
|
IEnumerable<Constraint> MakeConstraints(IEnumerable<GenericParameter> genericParameters) |
|
{ |
|
foreach (var gp in genericParameters) { |
|
Constraint c = new Constraint(); |
|
c.TypeParameter = CleanName(gp.Name); |
|
// class/struct must be first |
|
if (gp.HasReferenceTypeConstraint) |
|
c.BaseTypes.Add(new PrimitiveType("class")); |
|
if (gp.HasNotNullableValueTypeConstraint) |
|
c.BaseTypes.Add(new PrimitiveType("struct")); |
|
|
|
foreach (var constraintType in gp.Constraints) { |
|
if (gp.HasNotNullableValueTypeConstraint && constraintType.FullName == "System.ValueType") |
|
continue; |
|
c.BaseTypes.Add(ConvertType(constraintType)); |
|
} |
|
|
|
if (gp.HasDefaultConstructorConstraint && !gp.HasNotNullableValueTypeConstraint) |
|
c.BaseTypes.Add(new PrimitiveType("new")); // new() must be last |
|
if (c.BaseTypes.Any()) |
|
yield return c; |
|
} |
|
} |
|
|
|
ConstructorDeclaration CreateConstructor(MethodDefinition methodDef) |
|
{ |
|
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.Parameters)); |
|
astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, context); |
|
ConvertAttributes(astMethod, methodDef); |
|
return astMethod; |
|
} |
|
|
|
PropertyDeclaration CreateProperty(PropertyDefinition propDef) |
|
{ |
|
PropertyDeclaration astProp = new PropertyDeclaration(); |
|
astProp.AddAnnotation(propDef); |
|
astProp.Modifiers = ConvertModifiers(propDef.GetMethod ?? propDef.SetMethod); |
|
astProp.Name = CleanName(propDef.Name); |
|
astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); |
|
if (propDef.GetMethod != null) { |
|
astProp.Getter = new Accessor { |
|
Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod, context) |
|
}.WithAnnotation(propDef.GetMethod); |
|
ConvertAttributes(astProp.Getter, propDef.GetMethod); |
|
} |
|
if (propDef.SetMethod != null) { |
|
astProp.Setter = new Accessor { |
|
Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod, context) |
|
}.WithAnnotation(propDef.SetMethod); |
|
ConvertAttributes(astProp.Setter, propDef.SetMethod); |
|
ConvertCustomAttributes(astProp.Setter, propDef.SetMethod.Parameters.Last(), AttributeTarget.Param); |
|
} |
|
ConvertCustomAttributes(astProp, propDef); |
|
return astProp; |
|
} |
|
|
|
CustomEventDeclaration CreateEvent(EventDefinition eventDef) |
|
{ |
|
CustomEventDeclaration astEvent = new CustomEventDeclaration(); |
|
astEvent.AddAnnotation(eventDef); |
|
astEvent.Name = CleanName(eventDef.Name); |
|
astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef); |
|
astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); |
|
if (eventDef.AddMethod != null) { |
|
astEvent.AddAccessor = new Accessor { |
|
Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod, context) |
|
}.WithAnnotation(eventDef.AddMethod); |
|
ConvertAttributes(astEvent.AddAccessor, eventDef.AddMethod); |
|
} |
|
if (eventDef.RemoveMethod != null) { |
|
astEvent.RemoveAccessor = new Accessor { |
|
Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod, context) |
|
}.WithAnnotation(eventDef.RemoveMethod); |
|
ConvertAttributes(astEvent.RemoveAccessor, eventDef.RemoveMethod); |
|
} |
|
return astEvent; |
|
} |
|
|
|
FieldDeclaration CreateField(FieldDefinition fieldDef) |
|
{ |
|
FieldDeclaration astField = new FieldDeclaration(); |
|
astField.AddAnnotation(fieldDef); |
|
VariableInitializer initializer = new VariableInitializer(CleanName(fieldDef.Name)); |
|
astField.AddChild(initializer, FieldDeclaration.Roles.Variable); |
|
astField.ReturnType = ConvertType(fieldDef.FieldType, fieldDef); |
|
astField.Modifiers = ConvertModifiers(fieldDef); |
|
if (fieldDef.HasConstant) { |
|
if (fieldDef.Constant == null) |
|
initializer.Initializer = new NullReferenceExpression(); |
|
else |
|
initializer.Initializer = new PrimitiveExpression(fieldDef.Constant); |
|
} |
|
ConvertAttributes(astField, fieldDef); |
|
return astField; |
|
} |
|
|
|
public static IEnumerable<ParameterDeclaration> MakeParameters(IEnumerable<ParameterDefinition> paramCol) |
|
{ |
|
foreach(ParameterDefinition paramDef in paramCol) { |
|
ParameterDeclaration astParam = new ParameterDeclaration(); |
|
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; |
|
} |
|
// TODO: params, this |
|
|
|
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(AttributedNode attributedNode, TypeDefinition typeDefinition) |
|
{ |
|
ConvertCustomAttributes(attributedNode, typeDefinition); |
|
|
|
// Handle the non-custom attributes: |
|
#region SerializableAttribute |
|
if (typeDefinition.IsSerializable) |
|
attributedNode.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(SerializableAttribute)))); |
|
#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(AttributedNode attributedNode, MethodDefinition methodDefinition) |
|
{ |
|
ConvertCustomAttributes(attributedNode, methodDefinition); |
|
|
|
MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask; |
|
|
|
#region DllImportAttribute |
|
if (methodDefinition.HasPInvokeInfo) { |
|
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 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 |
|
|
|
ConvertCustomAttributes(attributedNode, methodDefinition.MethodReturnType, AttributeTarget.Return); |
|
if (methodDefinition.MethodReturnType.HasMarshalInfo) { |
|
var marshalInfo = ConvertMarshalInfo(methodDefinition.MethodReturnType, methodDefinition.Module); |
|
attributedNode.Attributes.Add(new AttributeSection(marshalInfo) { AttributeTarget = AttributeTarget.Return }); |
|
} |
|
} |
|
|
|
void ConvertAttributes(AttributedNode attributedNode, FieldDefinition fieldDefinition) |
|
{ |
|
ConvertCustomAttributes(attributedNode, fieldDefinition); |
|
|
|
#region FieldOffsetAttribute |
|
if (fieldDefinition.HasLayoutInfo) { |
|
Ast.Attribute fieldOffset = CreateNonCustomAttribute(typeof(FieldOffsetAttribute)); |
|
fieldOffset.Arguments.Add(new PrimitiveExpression(fieldDefinition.Offset)); |
|
attributedNode.Attributes.Add(new AttributeSection(fieldOffset)); |
|
} |
|
#endregion |
|
|
|
if (fieldDefinition.HasMarshalInfo) { |
|
attributedNode.Attributes.Add(new AttributeSection(ConvertMarshalInfo(fieldDefinition, fieldDefinition.Module))); |
|
} |
|
} |
|
|
|
#region MarshalAsAttribute (ConvertMarshalInfo) |
|
static Ast.Attribute ConvertMarshalInfo(IMarshalInfoProvider marshalInfoProvider, ModuleDefinition module) |
|
{ |
|
MarshalInfo marshalInfo = marshalInfoProvider.MarshalInfo; |
|
Ast.Attribute attr = CreateNonCustomAttribute(typeof(MarshalAsAttribute), module); |
|
string memberName; |
|
if (marshalInfo.NativeType == NativeType.Boolean) |
|
memberName = "Bool"; |
|
else |
|
memberName = marshalInfo.NativeType.ToString(); |
|
attr.Arguments.Add(new IdentifierExpression("UnmanagedType").Member(memberName)); |
|
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, AttributeTarget target = AttributeTarget.None) |
|
{ |
|
if (customAttributeProvider.HasCustomAttributes) { |
|
var attributes = new List<ICSharpCode.NRefactory.CSharp.Attribute>(); |
|
foreach (var customAttribute in customAttributeProvider.CustomAttributes) { |
|
var attribute = new ICSharpCode.NRefactory.CSharp.Attribute(); |
|
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 (target == AttributeTarget.Module || target == AttributeTarget.Assembly) { |
|
// use separate section for each attribute |
|
foreach (var attribute in attributes) { |
|
var section = new AttributeSection(); |
|
section.AttributeTarget = target; |
|
section.Attributes.Add(attribute); |
|
attributedNode.AddChild(section, AttributedNode.AttributeRole); |
|
} |
|
} else { |
|
// use single section for all attributes |
|
var section = new AttributeSection(); |
|
section.AttributeTarget = target; |
|
section.Attributes.AddRange(attributes); |
|
attributedNode.AddChild(section, AttributedNode.AttributeRole); |
|
} |
|
} |
|
} |
|
|
|
private static Expression ConvertArgumentValue(CustomAttributeArgument parameter) |
|
{ |
|
var type = parameter.Type.Resolve(); |
|
Expression parameterValue; |
|
if (type.IsEnum) |
|
{ |
|
parameterValue = MakePrimitive(Convert.ToInt64(parameter.Value), type); |
|
} |
|
else if (parameter.Value is TypeReference) |
|
{ |
|
parameterValue = new TypeOfExpression() |
|
{ |
|
Type = ConvertType((TypeReference)parameter.Value), |
|
}; |
|
} |
|
else |
|
{ |
|
parameterValue = new PrimitiveExpression(parameter.Value); |
|
} |
|
return parameterValue; |
|
} |
|
#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); |
|
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) |
|
{ |
|
foreach (FieldDefinition field in enumDefinition.Fields) |
|
{ |
|
if (field.IsStatic && object.Equals(CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false), val)) |
|
return ConvertType(enumDefinition).Member(field.Name).WithAnnotation(field); |
|
else if (!field.IsStatic && field.IsRuntimeSpecialName) |
|
type = field.FieldType; // use primitive type of the enum |
|
} |
|
if (IsFlagsEnum(enumDefinition)) |
|
{ |
|
long enumValue = val; |
|
Expression expr = 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(enumDefinition).Member(field.Name).WithAnnotation(field); |
|
if (expr == null) |
|
expr = fieldExpression; |
|
else |
|
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.BitwiseOr, fieldExpression); |
|
|
|
enumValue &= ~fieldValue; |
|
if (enumValue == 0) |
|
break; |
|
} |
|
} |
|
if(enumValue == 0 && expr != null) |
|
return expr; |
|
} |
|
TypeCode enumBaseTypeCode = TypeAnalysis.GetTypeCode(type); |
|
return new Ast.PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false)).CastTo(ConvertType(enumDefinition)); |
|
} |
|
} |
|
TypeCode code = TypeAnalysis.GetTypeCode(type); |
|
if (code == TypeCode.Object) |
|
return new Ast.PrimitiveExpression((int)val); |
|
else |
|
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"); |
|
} |
|
} |
|
}
|
|
|