summaryrefslogtreecommitdiff
path: root/ICSharpCode.Decompiler/Ast
diff options
context:
space:
mode:
Diffstat (limited to 'ICSharpCode.Decompiler/Ast')
-rw-r--r--ICSharpCode.Decompiler/Ast/Annotations.cs52
-rw-r--r--ICSharpCode.Decompiler/Ast/AstBuilder.cs1690
-rw-r--r--ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs1220
-rw-r--r--ICSharpCode.Decompiler/Ast/CommentStatement.cs69
-rw-r--r--ICSharpCode.Decompiler/Ast/DecompilerContext.cs71
-rw-r--r--ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs86
-rw-r--r--ICSharpCode.Decompiler/Ast/NameVariables.cs347
-rw-r--r--ICSharpCode.Decompiler/Ast/TextTokenWriter.cs369
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs368
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs179
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs111
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs183
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/CustomPatterns.cs109
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs58
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs368
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs502
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs875
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/FlattenSwitchBlocks.cs27
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/IntroduceExtensionMethods.cs66
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/IntroduceQueryExpressions.cs295
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs106
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs359
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs1123
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs164
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs356
-rw-r--r--ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs65
-rw-r--r--ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs528
27 files changed, 9746 insertions, 0 deletions
diff --git a/ICSharpCode.Decompiler/Ast/Annotations.cs b/ICSharpCode.Decompiler/Ast/Annotations.cs
new file mode 100644
index 00000000..ec9fd61f
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Annotations.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using ICSharpCode.Decompiler.ILAst;
+using ICSharpCode.NRefactory.CSharp;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast
+{
+ public class TypeInformation
+ {
+ public readonly TypeReference InferredType;
+ public readonly TypeReference ExpectedType;
+
+ public TypeInformation(TypeReference inferredType, TypeReference expectedType)
+ {
+ InferredType = inferredType;
+ ExpectedType = expectedType;
+ }
+ }
+
+ public class LdTokenAnnotation {}
+
+ /// <summary>
+ /// Annotation that is applied to the body expression of an Expression.Lambda() call.
+ /// </summary>
+ public class ParameterDeclarationAnnotation
+ {
+ public readonly List<ParameterDeclaration> Parameters = new List<ParameterDeclaration>();
+
+ public ParameterDeclarationAnnotation(ILExpression expr)
+ {
+ Debug.Assert(expr.Code == ILCode.ExpressionTreeParameterDeclarations);
+ for (int i = 0; i < expr.Arguments.Count - 1; i++) {
+ ILExpression p = expr.Arguments[i];
+ // p looks like this:
+ // stloc(v, call(Expression::Parameter, call(Type::GetTypeFromHandle, ldtoken(...)), ldstr(...)))
+ ILVariable v = (ILVariable)p.Operand;
+ TypeReference typeRef = (TypeReference)p.Arguments[0].Arguments[0].Arguments[0].Operand;
+ string name = (string)p.Arguments[0].Arguments[1].Operand;
+ Parameters.Add(new ParameterDeclaration(AstBuilder.ConvertType(typeRef), name).WithAnnotation(v));
+ }
+ }
+ }
+
+ /// <summary>
+ /// Annotation that is applied to a LambdaExpression that was produced by an expression tree.
+ /// </summary>
+ public class ExpressionTreeLambdaAnnotation
+ {
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs
new file mode 100644
index 00000000..faab2725
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs
@@ -0,0 +1,1690 @@
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.Ast.Transforms;
+using ICSharpCode.Decompiler.ILAst;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.Utils;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.Ast
+{
+ using Ast = NRefactory.CSharp;
+ using VarianceModifier = NRefactory.TypeSystem.VarianceModifier;
+
+ [Flags]
+ public enum ConvertTypeOptions
+ {
+ None = 0,
+ IncludeNamespace = 1,
+ IncludeTypeParameterDefinitions = 2,
+ DoNotUsePrimitiveTypeNames = 4
+ }
+
+ public class AstBuilder
+ {
+ DecompilerContext context;
+ SyntaxTree syntaxTree = new SyntaxTree();
+ Dictionary<string, NamespaceDeclaration> astNamespaces = new Dictionary<string, NamespaceDeclaration>();
+ bool transformationsHaveRun;
+
+ public AstBuilder(DecompilerContext context)
+ {
+ if (context == null)
+ throw new ArgumentNullException("context");
+ this.context = context;
+ DecompileMethodBodies = true;
+ }
+
+ public static bool MemberIsHidden(MemberReference member, DecompilerSettings settings)
+ {
+ MethodDefinition method = member as MethodDefinition;
+ if (method != null) {
+ if (method.IsGetter || method.IsSetter || method.IsAddOn || method.IsRemoveOn)
+ return true;
+ if (settings.AnonymousMethods && method.HasGeneratedName() && method.IsCompilerGenerated())
+ return true;
+ }
+
+ TypeDefinition type = member as TypeDefinition;
+ if (type != null) {
+ if (type.DeclaringType != null) {
+ if (settings.AnonymousMethods && IsClosureType(type))
+ return true;
+ if (settings.YieldReturn && YieldReturnDecompiler.IsCompilerGeneratorEnumerator(type))
+ return true;
+ if (settings.AsyncAwait && AsyncDecompiler.IsCompilerGeneratedStateMachine(type))
+ return true;
+ } else if (type.IsCompilerGenerated()) {
+ if (type.Name.StartsWith("<PrivateImplementationDetails>", StringComparison.Ordinal))
+ return true;
+ if (type.IsAnonymousType())
+ return true;
+ }
+ }
+
+ FieldDefinition field = member as FieldDefinition;
+ if (field != null) {
+ if (field.IsCompilerGenerated()) {
+ if (settings.AnonymousMethods && IsAnonymousMethodCacheField(field))
+ return true;
+ if (settings.AutomaticProperties && IsAutomaticPropertyBackingField(field))
+ return true;
+ if (settings.SwitchStatementOnString && IsSwitchOnStringCache(field))
+ return true;
+ }
+ // event-fields are not [CompilerGenerated]
+ if (settings.AutomaticEvents && field.DeclaringType.Events.Any(ev => ev.Name == field.Name))
+ return true;
+ }
+
+ return false;
+ }
+
+ static bool IsSwitchOnStringCache(FieldDefinition field)
+ {
+ return field.Name.StartsWith("<>f__switch", StringComparison.Ordinal);
+ }
+
+ static bool IsAutomaticPropertyBackingField(FieldDefinition field)
+ {
+ return field.HasGeneratedName() && field.Name.EndsWith("BackingField", StringComparison.Ordinal);
+ }
+
+ static bool IsAnonymousMethodCacheField(FieldDefinition field)
+ {
+ return field.Name.StartsWith("CS$<>", StringComparison.Ordinal) || field.Name.StartsWith("<>f__am", StringComparison.Ordinal);
+ }
+
+ static bool IsClosureType(TypeDefinition type)
+ {
+ return type.HasGeneratedName() && type.IsCompilerGenerated() && (type.Name.Contains("DisplayClass") || type.Name.Contains("AnonStorey"));
+ }
+
+ /// <summary>
+ /// Runs the C# transformations on the compilation unit.
+ /// </summary>
+ public void RunTransformations()
+ {
+ RunTransformations(null);
+ }
+
+ public void RunTransformations(Predicate<IAstTransform> transformAbortCondition)
+ {
+ TransformationPipeline.RunTransformationsUntil(syntaxTree, transformAbortCondition, context);
+ transformationsHaveRun = true;
+ }
+
+ /// <summary>
+ /// Gets the abstract source tree.
+ /// </summary>
+ public SyntaxTree SyntaxTree {
+ get { return syntaxTree; }
+ }
+
+ /// <summary>
+ /// Generates C# code from the abstract source tree.
+ /// </summary>
+ /// <remarks>This method adds ParenthesizedExpressions into the AST, and will run transformations if <see cref="RunTransformations"/> was not called explicitly</remarks>
+ public void GenerateCode(ITextOutput output)
+ {
+ if (!transformationsHaveRun)
+ RunTransformations();
+
+ syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true });
+ var outputFormatter = new TextTokenWriter(output, context) { FoldBraces = context.Settings.FoldBraces };
+ var formattingPolicy = context.Settings.CSharpFormattingOptions;
+ syntaxTree.AcceptVisitor(new CSharpOutputVisitor(outputFormatter, formattingPolicy));
+ }
+
+ public void AddAssembly(AssemblyDefinition assemblyDefinition, bool onlyAssemblyLevel = false)
+ {
+ AddAssembly(assemblyDefinition.MainModule, onlyAssemblyLevel);
+ }
+
+ public void AddAssembly(ModuleDefinition moduleDefinition, bool onlyAssemblyLevel = false)
+ {
+ if (moduleDefinition.Assembly != null && moduleDefinition.Assembly.Name.Version != null) {
+ syntaxTree.AddChild(
+ new AttributeSection {
+ AttributeTarget = "assembly",
+ Attributes = {
+ new NRefactory.CSharp.Attribute {
+ Type = new SimpleType("AssemblyVersion")
+ .WithAnnotation(new TypeReference(
+ "System.Reflection", "AssemblyVersionAttribute",
+ moduleDefinition, moduleDefinition.TypeSystem.Corlib)),
+ Arguments = {
+ new PrimitiveExpression(moduleDefinition.Assembly.Name.Version.ToString())
+ }
+ }
+ }
+ }, EntityDeclaration.AttributeRole);
+ }
+
+ if (moduleDefinition.Assembly != null) {
+ ConvertCustomAttributes(syntaxTree, moduleDefinition.Assembly, "assembly");
+ ConvertSecurityAttributes(syntaxTree, moduleDefinition.Assembly, "assembly");
+ }
+ ConvertCustomAttributes(syntaxTree, moduleDefinition, "module");
+ AddTypeForwarderAttributes(syntaxTree, moduleDefinition, "assembly");
+
+ if (!onlyAssemblyLevel) {
+ foreach (TypeDefinition typeDef in moduleDefinition.Types) {
+ // Skip the <Module> class
+ if (typeDef.Name == "<Module>") continue;
+ // Skip any hidden types
+ if (MemberIsHidden(typeDef, context.Settings))
+ continue;
+
+ AddType(typeDef);
+ }
+ }
+ }
+
+ void AddTypeForwarderAttributes(SyntaxTree astCompileUnit, ModuleDefinition module, string target)
+ {
+ if (!module.HasExportedTypes)
+ return;
+ foreach (ExportedType type in module.ExportedTypes) {
+ if (type.IsForwarder) {
+ var forwardedType = CreateTypeOfExpression(new TypeReference(type.Namespace, type.Name, module, type.Scope));
+ astCompileUnit.AddChild(
+ new AttributeSection {
+ AttributeTarget = target,
+ Attributes = {
+ new NRefactory.CSharp.Attribute {
+ Type = new SimpleType("TypeForwardedTo")
+ .WithAnnotation(new TypeReference(
+ "System.Runtime.CompilerServices", "TypeForwardedToAttribute",
+ module, module.TypeSystem.Corlib)),
+ Arguments = { forwardedType }
+ }
+ }
+ }, EntityDeclaration.AttributeRole);
+ }
+ }
+ }
+
+ NamespaceDeclaration GetCodeNamespace(string name)
+ {
+ if (string.IsNullOrEmpty(name)) {
+ return null;
+ }
+ if (astNamespaces.ContainsKey(name)) {
+ return astNamespaces[name];
+ } else {
+ // Create the namespace
+ NamespaceDeclaration astNamespace = new NamespaceDeclaration { Name = name };
+ syntaxTree.Members.Add(astNamespace);
+ astNamespaces[name] = astNamespace;
+ return astNamespace;
+ }
+ }
+
+ public void AddType(TypeDefinition typeDef)
+ {
+ var astType = CreateType(typeDef);
+ NamespaceDeclaration astNS = GetCodeNamespace(typeDef.Namespace);
+ if (astNS != null) {
+ astNS.Members.Add(astType);
+ } else {
+ syntaxTree.Members.Add(astType);
+ }
+ }
+
+ public void AddMethod(MethodDefinition method)
+ {
+ AstNode node = method.IsConstructor ? (AstNode)CreateConstructor(method) : CreateMethod(method);
+ syntaxTree.Members.Add(node);
+ }
+
+ public void AddProperty(PropertyDefinition property)
+ {
+ syntaxTree.Members.Add(CreateProperty(property));
+ }
+
+ public void AddField(FieldDefinition field)
+ {
+ syntaxTree.Members.Add(CreateField(field));
+ }
+
+ public void AddEvent(EventDefinition ev)
+ {
+ syntaxTree.Members.Add(CreateEvent(ev));
+ }
+
+ /// <summary>
+ /// Creates the AST for a type definition.
+ /// </summary>
+ /// <param name="typeDef"></param>
+ /// <returns>TypeDeclaration or DelegateDeclaration.</returns>
+ public EntityDeclaration CreateType(TypeDefinition typeDef)
+ {
+ // create type
+ TypeDefinition oldCurrentType = context.CurrentType;
+ context.CurrentType = typeDef;
+ TypeDeclaration astType = new TypeDeclaration();
+ ConvertAttributes(astType, typeDef);
+ astType.AddAnnotation(typeDef);
+ astType.Modifiers = ConvertModifiers(typeDef);
+ astType.Name = CleanName(typeDef.Name);
+
+ if (typeDef.IsEnum) { // NB: Enum is value type
+ astType.ClassType = ClassType.Enum;
+ astType.Modifiers &= ~Modifiers.Sealed;
+ } else if (typeDef.IsValueType) {
+ astType.ClassType = ClassType.Struct;
+ astType.Modifiers &= ~Modifiers.Sealed;
+ } else if (typeDef.IsInterface) {
+ astType.ClassType = ClassType.Interface;
+ astType.Modifiers &= ~Modifiers.Abstract;
+ } else {
+ astType.ClassType = ClassType.Class;
+ }
+
+ IEnumerable<GenericParameter> genericParameters = typeDef.GenericParameters;
+ if (typeDef.DeclaringType != null && typeDef.DeclaringType.HasGenericParameters)
+ genericParameters = genericParameters.Skip(typeDef.DeclaringType.GenericParameters.Count);
+ astType.TypeParameters.AddRange(MakeTypeParameters(genericParameters));
+ astType.Constraints.AddRange(MakeConstraints(genericParameters));
+
+ EntityDeclaration result = astType;
+ if (typeDef.IsEnum) {
+ long expectedEnumMemberValue = 0;
+ bool forcePrintingInitializers = IsFlagsEnum(typeDef);
+ TypeCode baseType = TypeCode.Int32;
+ foreach (FieldDefinition field in typeDef.Fields) {
+ if (!field.IsStatic) {
+ // the value__ field
+ if (field.FieldType != typeDef.Module.TypeSystem.Int32) {
+ astType.AddChild(ConvertType(field.FieldType), Roles.BaseType);
+ baseType = TypeAnalysis.GetTypeCode(field.FieldType);
+ }
+ } else {
+ EnumMemberDeclaration enumMember = new EnumMemberDeclaration();
+ enumMember.AddAnnotation(field);
+ enumMember.Name = CleanName(field.Name);
+ long memberValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false);
+ if (forcePrintingInitializers || memberValue != expectedEnumMemberValue) {
+ enumMember.AddChild(new PrimitiveExpression(CSharpPrimitiveCast.Cast(baseType, field.Constant, false)), EnumMemberDeclaration.InitializerRole);
+ }
+ expectedEnumMemberValue = memberValue + 1;
+ astType.AddChild(enumMember, Roles.TypeMemberRole);
+ }
+ }
+ } else if (typeDef.BaseType != null && typeDef.BaseType.FullName == "System.MulticastDelegate") {
+ DelegateDeclaration dd = new DelegateDeclaration();
+ dd.Modifiers = astType.Modifiers & ~Modifiers.Sealed;
+ dd.Name = astType.Name;
+ dd.AddAnnotation(typeDef);
+ astType.Attributes.MoveTo(dd.Attributes);
+ astType.TypeParameters.MoveTo(dd.TypeParameters);
+ astType.Constraints.MoveTo(dd.Constraints);
+ foreach (var m in typeDef.Methods) {
+ if (m.Name == "Invoke") {
+ dd.ReturnType = ConvertType(m.ReturnType, m.MethodReturnType);
+ dd.Parameters.AddRange(MakeParameters(m));
+ ConvertAttributes(dd, m.MethodReturnType, m.Module);
+ }
+ }
+ result = dd;
+ } else {
+ // Base type
+ if (typeDef.BaseType != null && !typeDef.IsValueType && typeDef.BaseType.FullName != "System.Object") {
+ astType.AddChild(ConvertType(typeDef.BaseType), Roles.BaseType);
+ }
+ foreach (var i in typeDef.Interfaces)
+ astType.AddChild(ConvertType(i), Roles.BaseType);
+
+ AddTypeMembers(astType, typeDef);
+
+ if (astType.Members.OfType<IndexerDeclaration>().Any(idx => idx.PrivateImplementationType.IsNull)) {
+ // Remove the [DefaultMember] attribute if the class contains indexers
+ foreach (AttributeSection section in astType.Attributes) {
+ foreach (Ast.Attribute attr in section.Attributes) {
+ TypeReference tr = attr.Type.Annotation<TypeReference>();
+ if (tr != null && tr.Name == "DefaultMemberAttribute" && tr.Namespace == "System.Reflection") {
+ attr.Remove();
+ }
+ }
+ if (section.Attributes.Count == 0)
+ section.Remove();
+ }
+ }
+ }
+
+ context.CurrentType = oldCurrentType;
+ return result;
+ }
+
+ internal static string CleanName(string name)
+ {
+ int pos = name.LastIndexOf('`');
+ if (pos >= 0)
+ name = name.Substring(0, pos);
+ pos = name.LastIndexOf('.');
+ if (pos >= 0)
+ name = name.Substring(pos + 1);
+ return name;
+ }
+
+ #region Create TypeOf Expression
+ /// <summary>
+ /// Creates a typeof-expression for the specified type.
+ /// </summary>
+ public static TypeOfExpression CreateTypeOfExpression(TypeReference type)
+ {
+ return new TypeOfExpression(AddEmptyTypeArgumentsForUnboundGenerics(ConvertType(type)));
+ }
+
+ static AstType AddEmptyTypeArgumentsForUnboundGenerics(AstType type)
+ {
+ TypeReference typeRef = type.Annotation<TypeReference>();
+ if (typeRef == null)
+ return type;
+ TypeDefinition typeDef = typeRef.Resolve(); // need to resolve to figure out the number of type parameters
+ if (typeDef == null || !typeDef.HasGenericParameters)
+ return type;
+ SimpleType sType = type as SimpleType;
+ MemberType mType = type as MemberType;
+ if (sType != null) {
+ while (typeDef.GenericParameters.Count > sType.TypeArguments.Count) {
+ sType.TypeArguments.Add(new SimpleType(""));
+ }
+ }
+
+ if (mType != null) {
+ AddEmptyTypeArgumentsForUnboundGenerics(mType.Target);
+
+ int outerTypeParamCount = typeDef.DeclaringType == null ? 0 : typeDef.DeclaringType.GenericParameters.Count;
+
+ while (typeDef.GenericParameters.Count - outerTypeParamCount > mType.TypeArguments.Count) {
+ mType.TypeArguments.Add(new SimpleType(""));
+ }
+ }
+
+ return type;
+ }
+ #endregion
+
+ #region Convert Type Reference
+ /// <summary>
+ /// Converts a type reference.
+ /// </summary>
+ /// <param name="type">The Cecil type reference that should be converted into
+ /// a type system type reference.</param>
+ /// <param name="typeAttributes">Attributes associated with the Cecil type reference.
+ /// This is used to support the 'dynamic' type.</param>
+ public static AstType ConvertType(TypeReference type, ICustomAttributeProvider typeAttributes = null, ConvertTypeOptions options = ConvertTypeOptions.None)
+ {
+ int typeIndex = 0;
+ return ConvertType(type, typeAttributes, ref typeIndex, options);
+ }
+
+ static AstType ConvertType(TypeReference type, ICustomAttributeProvider typeAttributes, ref int typeIndex, ConvertTypeOptions options)
+ {
+ while (type is OptionalModifierType || type is RequiredModifierType) {
+ type = ((TypeSpecification)type).ElementType;
+ }
+ if (type == null) {
+ return AstType.Null;
+ }
+
+ if (type is ByReferenceType) {
+ typeIndex++;
+ // by reference type cannot be represented in C#; so we'll represent it as a pointer instead
+ return ConvertType((type as ByReferenceType).ElementType, typeAttributes, ref typeIndex, options)
+ .MakePointerType();
+ } else if (type is PointerType) {
+ typeIndex++;
+ return ConvertType((type as PointerType).ElementType, typeAttributes, ref typeIndex, options)
+ .MakePointerType();
+ } else if (type is ArrayType) {
+ typeIndex++;
+ return ConvertType((type as ArrayType).ElementType, typeAttributes, ref typeIndex, options)
+ .MakeArrayType((type as ArrayType).Rank);
+ } else if (type is GenericInstanceType) {
+ GenericInstanceType gType = (GenericInstanceType)type;
+ if (gType.ElementType.Namespace == "System" && gType.ElementType.Name == "Nullable`1" && gType.GenericArguments.Count == 1) {
+ typeIndex++;
+ return new ComposedType {
+ BaseType = ConvertType(gType.GenericArguments[0], typeAttributes, ref typeIndex, options),
+ HasNullableSpecifier = true
+ };
+ }
+ AstType baseType = ConvertType(gType.ElementType, typeAttributes, ref typeIndex, options & ~ConvertTypeOptions.IncludeTypeParameterDefinitions);
+ List<AstType> typeArguments = new List<AstType>();
+ foreach (var typeArgument in gType.GenericArguments) {
+ typeIndex++;
+ typeArguments.Add(ConvertType(typeArgument, typeAttributes, ref typeIndex, options));
+ }
+ ApplyTypeArgumentsTo(baseType, typeArguments);
+ return baseType;
+ } else if (type is GenericParameter) {
+ return new SimpleType(type.Name);
+ } else if (type.IsNested) {
+ AstType typeRef = ConvertType(type.DeclaringType, typeAttributes, ref typeIndex, options & ~ConvertTypeOptions.IncludeTypeParameterDefinitions);
+ string namepart = NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name);
+ MemberType memberType = new MemberType { Target = typeRef, MemberName = namepart };
+ memberType.AddAnnotation(type);
+ if ((options & ConvertTypeOptions.IncludeTypeParameterDefinitions) == ConvertTypeOptions.IncludeTypeParameterDefinitions) {
+ AddTypeParameterDefininitionsTo(type, memberType);
+ }
+ return memberType;
+ } else {
+ string ns = type.Namespace ?? string.Empty;
+ string name = type.Name;
+ if (name == null)
+ throw new InvalidOperationException("type.Name returned null. Type: " + type.ToString());
+
+ if (name == "Object" && ns == "System" && HasDynamicAttribute(typeAttributes, typeIndex)) {
+ return new PrimitiveType("dynamic");
+ } else {
+ if (ns == "System") {
+ if ((options & ConvertTypeOptions.DoNotUsePrimitiveTypeNames)
+ != ConvertTypeOptions.DoNotUsePrimitiveTypeNames) {
+ switch (name) {
+ case "SByte":
+ return new PrimitiveType("sbyte");
+ case "Int16":
+ return new PrimitiveType("short");
+ case "Int32":
+ return new PrimitiveType("int");
+ case "Int64":
+ return new PrimitiveType("long");
+ case "Byte":
+ return new PrimitiveType("byte");
+ case "UInt16":
+ return new PrimitiveType("ushort");
+ case "UInt32":
+ return new PrimitiveType("uint");
+ case "UInt64":
+ return new PrimitiveType("ulong");
+ case "String":
+ return new PrimitiveType("string");
+ case "Single":
+ return new PrimitiveType("float");
+ case "Double":
+ return new PrimitiveType("double");
+ case "Decimal":
+ return new PrimitiveType("decimal");
+ case "Char":
+ return new PrimitiveType("char");
+ case "Boolean":
+ return new PrimitiveType("bool");
+ case "Void":
+ return new PrimitiveType("void");
+ case "Object":
+ return new PrimitiveType("object");
+ }
+ }
+ }
+
+ name = NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(name);
+
+ AstType astType;
+ if ((options & ConvertTypeOptions.IncludeNamespace) == ConvertTypeOptions.IncludeNamespace && ns.Length > 0) {
+ string[] parts = ns.Split('.');
+ AstType nsType = new SimpleType(parts[0]);
+ for (int i = 1; i < parts.Length; i++) {
+ nsType = new MemberType { Target = nsType, MemberName = parts[i] };
+ }
+ astType = new MemberType { Target = nsType, MemberName = name };
+ } else {
+ astType = new SimpleType(name);
+ }
+ astType.AddAnnotation(type);
+
+ if ((options & ConvertTypeOptions.IncludeTypeParameterDefinitions) == ConvertTypeOptions.IncludeTypeParameterDefinitions) {
+ AddTypeParameterDefininitionsTo(type, astType);
+ }
+ return astType;
+ }
+ }
+ }
+
+ static void AddTypeParameterDefininitionsTo(TypeReference type, AstType astType)
+ {
+ if (type.HasGenericParameters) {
+ List<AstType> typeArguments = new List<AstType>();
+ foreach (GenericParameter gp in type.GenericParameters) {
+ typeArguments.Add(new SimpleType(gp.Name));
+ }
+ ApplyTypeArgumentsTo(astType, typeArguments);
+ }
+ }
+
+ static void ApplyTypeArgumentsTo(AstType baseType, List<AstType> typeArguments)
+ {
+ SimpleType st = baseType as SimpleType;
+ if (st != null) {
+ st.TypeArguments.AddRange(typeArguments);
+ }
+ MemberType mt = baseType as MemberType;
+ if (mt != null) {
+ TypeReference type = mt.Annotation<TypeReference>();
+ if (type != null) {
+ int typeParameterCount;
+ NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out typeParameterCount);
+ if (typeParameterCount > typeArguments.Count)
+ typeParameterCount = typeArguments.Count;
+ mt.TypeArguments.AddRange(typeArguments.GetRange(typeArguments.Count - typeParameterCount, typeParameterCount));
+ typeArguments.RemoveRange(typeArguments.Count - typeParameterCount, typeParameterCount);
+ if (typeArguments.Count > 0)
+ ApplyTypeArgumentsTo(mt.Target, typeArguments);
+ } else {
+ mt.TypeArguments.AddRange(typeArguments);
+ }
+ }
+ }
+
+ const string DynamicAttributeFullName = "System.Runtime.CompilerServices.DynamicAttribute";
+
+ static bool HasDynamicAttribute(ICustomAttributeProvider attributeProvider, int typeIndex)
+ {
+ if (attributeProvider == null || !attributeProvider.HasCustomAttributes)
+ return false;
+ foreach (CustomAttribute a in attributeProvider.CustomAttributes) {
+ if (a.Constructor.DeclaringType.FullName == DynamicAttributeFullName) {
+ if (a.ConstructorArguments.Count == 1) {
+ CustomAttributeArgument[] values = a.ConstructorArguments[0].Value as CustomAttributeArgument[];
+ if (values != null && typeIndex < values.Length && values[typeIndex].Value is bool)
+ return (bool)values[typeIndex].Value;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+ #endregion
+
+ #region ConvertModifiers
+ Modifiers ConvertModifiers(TypeDefinition typeDef)
+ {
+ Modifiers modifiers = Modifiers.None;
+ if (typeDef.IsNestedPrivate)
+ modifiers |= Modifiers.Private;
+ else if (typeDef.IsNestedAssembly || typeDef.IsNestedFamilyAndAssembly || typeDef.IsNotPublic)
+ modifiers |= Modifiers.Internal;
+ else if (typeDef.IsNestedFamily)
+ modifiers |= Modifiers.Protected;
+ else if (typeDef.IsNestedFamilyOrAssembly)
+ modifiers |= Modifiers.Protected | Modifiers.Internal;
+ else if (typeDef.IsPublic || typeDef.IsNestedPublic)
+ modifiers |= Modifiers.Public;
+
+ if (typeDef.IsAbstract && typeDef.IsSealed)
+ modifiers |= Modifiers.Static;
+ else if (typeDef.IsAbstract)
+ modifiers |= Modifiers.Abstract;
+ else if (typeDef.IsSealed)
+ modifiers |= Modifiers.Sealed;
+
+ return modifiers;
+ }
+
+ Modifiers ConvertModifiers(FieldDefinition fieldDef)
+ {
+ Modifiers modifiers = Modifiers.None;
+ if (fieldDef.IsPrivate)
+ modifiers |= Modifiers.Private;
+ else if (fieldDef.IsAssembly || fieldDef.IsFamilyAndAssembly)
+ modifiers |= Modifiers.Internal;
+ else if (fieldDef.IsFamily)
+ modifiers |= Modifiers.Protected;
+ else if (fieldDef.IsFamilyOrAssembly)
+ modifiers |= Modifiers.Protected | Modifiers.Internal;
+ else if (fieldDef.IsPublic)
+ modifiers |= Modifiers.Public;
+
+ if (fieldDef.IsLiteral) {
+ modifiers |= Modifiers.Const;
+ } else {
+ if (fieldDef.IsStatic)
+ modifiers |= Modifiers.Static;
+
+ if (fieldDef.IsInitOnly)
+ modifiers |= Modifiers.Readonly;
+ }
+
+ RequiredModifierType modreq = fieldDef.FieldType as RequiredModifierType;
+ if (modreq != null && modreq.ModifierType.FullName == typeof(IsVolatile).FullName)
+ modifiers |= Modifiers.Volatile;
+
+ return modifiers;
+ }
+
+ Modifiers ConvertModifiers(MethodDefinition methodDef)
+ {
+ if (methodDef == null)
+ return Modifiers.None;
+ Modifiers modifiers = Modifiers.None;
+ if (methodDef.IsPrivate)
+ modifiers |= Modifiers.Private;
+ else if (methodDef.IsAssembly || methodDef.IsFamilyAndAssembly)
+ modifiers |= Modifiers.Internal;
+ else if (methodDef.IsFamily)
+ modifiers |= Modifiers.Protected;
+ else if (methodDef.IsFamilyOrAssembly)
+ modifiers |= Modifiers.Protected | Modifiers.Internal;
+ else if (methodDef.IsPublic)
+ modifiers |= Modifiers.Public;
+
+ if (methodDef.IsStatic)
+ modifiers |= Modifiers.Static;
+
+ if (methodDef.IsAbstract) {
+ modifiers |= Modifiers.Abstract;
+ if (!methodDef.IsNewSlot)
+ modifiers |= Modifiers.Override;
+ } else if (methodDef.IsFinal) {
+ if (!methodDef.IsNewSlot) {
+ modifiers |= Modifiers.Sealed | Modifiers.Override;
+ }
+ } else if (methodDef.IsVirtual) {
+ if (methodDef.IsNewSlot)
+ modifiers |= Modifiers.Virtual;
+ else
+ modifiers |= Modifiers.Override;
+ }
+ if (!methodDef.HasBody && !methodDef.IsAbstract)
+ modifiers |= Modifiers.Extern;
+
+ return modifiers;
+ }
+
+ #endregion
+
+ void AddTypeMembers(TypeDeclaration astType, TypeDefinition typeDef)
+ {
+ // Nested types
+ foreach (TypeDefinition nestedTypeDef in typeDef.NestedTypes) {
+ if (MemberIsHidden(nestedTypeDef, context.Settings))
+ continue;
+ var nestedType = CreateType(nestedTypeDef);
+ SetNewModifier(nestedType);
+ astType.AddChild(nestedType, Roles.TypeMemberRole);
+ }
+
+ // Add fields
+ foreach(FieldDefinition fieldDef in typeDef.Fields) {
+ if (MemberIsHidden(fieldDef, context.Settings)) continue;
+ astType.AddChild(CreateField(fieldDef), Roles.TypeMemberRole);
+ }
+
+ // Add events
+ foreach(EventDefinition eventDef in typeDef.Events) {
+ astType.AddChild(CreateEvent(eventDef), Roles.TypeMemberRole);
+ }
+
+ // Add properties
+ foreach(PropertyDefinition propDef in typeDef.Properties) {
+ astType.Members.Add(CreateProperty(propDef));
+ }
+
+ // Add methods
+ foreach(MethodDefinition methodDef in typeDef.Methods) {
+ if (MemberIsHidden(methodDef, context.Settings)) continue;
+
+ if (methodDef.IsConstructor)
+ astType.Members.Add(CreateConstructor(methodDef));
+ else
+ astType.Members.Add(CreateMethod(methodDef));
+ }
+ }
+
+ EntityDeclaration CreateMethod(MethodDefinition methodDef)
+ {
+ MethodDeclaration astMethod = new MethodDeclaration();
+ astMethod.AddAnnotation(methodDef);
+ astMethod.ReturnType = ConvertType(methodDef.ReturnType, methodDef.MethodReturnType);
+ astMethod.Name = CleanName(methodDef.Name);
+ astMethod.TypeParameters.AddRange(MakeTypeParameters(methodDef.GenericParameters));
+ astMethod.Parameters.AddRange(MakeParameters(methodDef));
+ // constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly
+ if (!methodDef.IsVirtual || (methodDef.IsNewSlot && !methodDef.IsPrivate)) astMethod.Constraints.AddRange(MakeConstraints(methodDef.GenericParameters));
+ if (!methodDef.DeclaringType.IsInterface) {
+ if (IsExplicitInterfaceImplementation(methodDef)) {
+ astMethod.PrivateImplementationType = ConvertType(methodDef.Overrides.First().DeclaringType);
+ } else {
+ astMethod.Modifiers = ConvertModifiers(methodDef);
+ if (methodDef.IsVirtual == methodDef.IsNewSlot)
+ SetNewModifier(astMethod);
+ }
+ astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters);
+ if (context.CurrentMethodIsAsync) {
+ astMethod.Modifiers |= Modifiers.Async;
+ context.CurrentMethodIsAsync = false;
+ }
+ }
+ ConvertAttributes(astMethod, methodDef);
+ if (methodDef.HasCustomAttributes && astMethod.Parameters.Count > 0) {
+ foreach (CustomAttribute ca in methodDef.CustomAttributes) {
+ if (ca.AttributeType.Name == "ExtensionAttribute" && ca.AttributeType.Namespace == "System.Runtime.CompilerServices") {
+ astMethod.Parameters.First().ParameterModifier = ParameterModifier.This;
+ }
+ }
+ }
+
+ // Convert MethodDeclaration to OperatorDeclaration if possible
+ if (methodDef.IsSpecialName && !methodDef.HasGenericParameters) {
+ OperatorType? opType = OperatorDeclaration.GetOperatorType(methodDef.Name);
+ if (opType.HasValue) {
+ OperatorDeclaration op = new OperatorDeclaration();
+ op.CopyAnnotationsFrom(astMethod);
+ op.ReturnType = astMethod.ReturnType.Detach();
+ op.OperatorType = opType.Value;
+ op.Modifiers = astMethod.Modifiers;
+ astMethod.Parameters.MoveTo(op.Parameters);
+ astMethod.Attributes.MoveTo(op.Attributes);
+ op.Body = astMethod.Body.Detach();
+ return op;
+ }
+ }
+ return astMethod;
+ }
+
+ bool IsExplicitInterfaceImplementation(MethodDefinition methodDef)
+ {
+ return methodDef.HasOverrides && methodDef.IsPrivate;
+ }
+
+ IEnumerable<TypeParameterDeclaration> MakeTypeParameters(IEnumerable<GenericParameter> genericParameters)
+ {
+ foreach (var gp in genericParameters) {
+ TypeParameterDeclaration tp = new TypeParameterDeclaration();
+ tp.Name = CleanName(gp.Name);
+ if (gp.IsContravariant)
+ tp.Variance = VarianceModifier.Contravariant;
+ else if (gp.IsCovariant)
+ tp.Variance = VarianceModifier.Covariant;
+ ConvertCustomAttributes(tp, gp);
+ yield return tp;
+ }
+ }
+
+ IEnumerable<Constraint> MakeConstraints(IEnumerable<GenericParameter> genericParameters)
+ {
+ foreach (var gp in genericParameters) {
+ Constraint c = new Constraint();
+ c.TypeParameter = new SimpleType(CleanName(gp.Name));
+ // class/struct must be first
+ if (gp.HasReferenceTypeConstraint)
+ c.BaseTypes.Add(new PrimitiveType("class"));
+ if (gp.HasNotNullableValueTypeConstraint)
+ c.BaseTypes.Add(new PrimitiveType("struct"));
+
+ foreach (var constraintType in gp.Constraints) {
+ if (gp.HasNotNullableValueTypeConstraint && constraintType.FullName == "System.ValueType")
+ continue;
+ c.BaseTypes.Add(ConvertType(constraintType));
+ }
+
+ if (gp.HasDefaultConstructorConstraint && !gp.HasNotNullableValueTypeConstraint)
+ c.BaseTypes.Add(new PrimitiveType("new")); // new() must be last
+ if (c.BaseTypes.Any())
+ yield return c;
+ }
+ }
+
+ ConstructorDeclaration CreateConstructor(MethodDefinition methodDef)
+ {
+ ConstructorDeclaration astMethod = new ConstructorDeclaration();
+ astMethod.AddAnnotation(methodDef);
+ astMethod.Modifiers = ConvertModifiers(methodDef);
+ if (methodDef.IsStatic) {
+ // don't show visibility for static ctors
+ astMethod.Modifiers &= ~Modifiers.VisibilityMask;
+ }
+ astMethod.Name = CleanName(methodDef.DeclaringType.Name);
+ astMethod.Parameters.AddRange(MakeParameters(methodDef));
+ astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters);
+ ConvertAttributes(astMethod, methodDef);
+ if (methodDef.IsStatic && methodDef.DeclaringType.IsBeforeFieldInit && !astMethod.Body.IsNull) {
+ astMethod.Body.InsertChildAfter(null, new Comment(" Note: this type is marked as 'beforefieldinit'."), Roles.Comment);
+ }
+ return astMethod;
+ }
+
+ Modifiers FixUpVisibility(Modifiers m)
+ {
+ Modifiers v = m & Modifiers.VisibilityMask;
+ // If any of the modifiers is public, use that
+ if ((v & Modifiers.Public) == Modifiers.Public)
+ return Modifiers.Public | (m & ~Modifiers.VisibilityMask);
+ // If both modifiers are private, no need to fix anything
+ if (v == Modifiers.Private)
+ return m;
+ // Otherwise, use the other modifiers (internal and/or protected)
+ return m & ~Modifiers.Private;
+ }
+
+ EntityDeclaration CreateProperty(PropertyDefinition propDef)
+ {
+ PropertyDeclaration astProp = new PropertyDeclaration();
+ astProp.AddAnnotation(propDef);
+ var accessor = propDef.GetMethod ?? propDef.SetMethod;
+ Modifiers getterModifiers = Modifiers.None;
+ Modifiers setterModifiers = Modifiers.None;
+ if (IsExplicitInterfaceImplementation(accessor)) {
+ astProp.PrivateImplementationType = ConvertType(accessor.Overrides.First().DeclaringType);
+ } else if (!propDef.DeclaringType.IsInterface) {
+ getterModifiers = ConvertModifiers(propDef.GetMethod);
+ setterModifiers = ConvertModifiers(propDef.SetMethod);
+ astProp.Modifiers = FixUpVisibility(getterModifiers | setterModifiers);
+ try {
+ if (accessor.IsVirtual && !accessor.IsNewSlot && (propDef.GetMethod == null || propDef.SetMethod == null)) {
+ foreach (var basePropDef in TypesHierarchyHelpers.FindBaseProperties(propDef)) {
+ if (basePropDef.GetMethod != null && basePropDef.SetMethod != null) {
+ var propVisibilityModifiers = ConvertModifiers(basePropDef.GetMethod) | ConvertModifiers(basePropDef.SetMethod);
+ astProp.Modifiers = FixUpVisibility((astProp.Modifiers & ~Modifiers.VisibilityMask) | (propVisibilityModifiers & Modifiers.VisibilityMask));
+ break;
+ } else if ((basePropDef.GetMethod ?? basePropDef.SetMethod).IsNewSlot) {
+ break;
+ }
+ }
+ }
+ } catch (ReferenceResolvingException) {
+ // TODO: add some kind of notification (a comment?) about possible problems with decompiled code due to unresolved references.
+ }
+ }
+ astProp.Name = CleanName(propDef.Name);
+ astProp.ReturnType = ConvertType(propDef.PropertyType, propDef);
+
+ if (propDef.GetMethod != null) {
+ astProp.Getter = new Accessor();
+ astProp.Getter.Body = CreateMethodBody(propDef.GetMethod);
+ astProp.Getter.AddAnnotation(propDef.GetMethod);
+ ConvertAttributes(astProp.Getter, propDef.GetMethod);
+
+ if ((getterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask))
+ astProp.Getter.Modifiers = getterModifiers & Modifiers.VisibilityMask;
+ }
+ if (propDef.SetMethod != null) {
+ astProp.Setter = new Accessor();
+ astProp.Setter.Body = CreateMethodBody(propDef.SetMethod);
+ astProp.Setter.AddAnnotation(propDef.SetMethod);
+ ConvertAttributes(astProp.Setter, propDef.SetMethod);
+ ParameterDefinition lastParam = propDef.SetMethod.Parameters.LastOrDefault();
+ if (lastParam != null) {
+ ConvertCustomAttributes(astProp.Setter, lastParam, "param");
+ if (lastParam.HasMarshalInfo) {
+ astProp.Setter.Attributes.Add(new AttributeSection(ConvertMarshalInfo(lastParam, propDef.Module)) { AttributeTarget = "param" });
+ }
+ }
+
+ if ((setterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask))
+ astProp.Setter.Modifiers = setterModifiers & Modifiers.VisibilityMask;
+ }
+ ConvertCustomAttributes(astProp, propDef);
+
+ EntityDeclaration member = astProp;
+ if(propDef.IsIndexer())
+ member = ConvertPropertyToIndexer(astProp, propDef);
+ if(!accessor.HasOverrides && !accessor.DeclaringType.IsInterface)
+ if (accessor.IsVirtual == accessor.IsNewSlot)
+ SetNewModifier(member);
+ return member;
+ }
+
+ IndexerDeclaration ConvertPropertyToIndexer(PropertyDeclaration astProp, PropertyDefinition propDef)
+ {
+ var astIndexer = new IndexerDeclaration();
+ astIndexer.CopyAnnotationsFrom(astProp);
+ astProp.Attributes.MoveTo(astIndexer.Attributes);
+ astIndexer.Modifiers = astProp.Modifiers;
+ astIndexer.PrivateImplementationType = astProp.PrivateImplementationType.Detach();
+ astIndexer.ReturnType = astProp.ReturnType.Detach();
+ astIndexer.Getter = astProp.Getter.Detach();
+ astIndexer.Setter = astProp.Setter.Detach();
+ astIndexer.Parameters.AddRange(MakeParameters(propDef.Parameters));
+ return astIndexer;
+ }
+
+ EntityDeclaration CreateEvent(EventDefinition eventDef)
+ {
+ if (eventDef.AddMethod != null && eventDef.AddMethod.IsAbstract) {
+ // An abstract event cannot be custom
+ EventDeclaration astEvent = new EventDeclaration();
+ ConvertCustomAttributes(astEvent, eventDef);
+ astEvent.AddAnnotation(eventDef);
+ astEvent.Variables.Add(new VariableInitializer(CleanName(eventDef.Name)));
+ astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef);
+ if (!eventDef.DeclaringType.IsInterface)
+ astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod);
+ return astEvent;
+ } else {
+ CustomEventDeclaration astEvent = new CustomEventDeclaration();
+ ConvertCustomAttributes(astEvent, eventDef);
+ astEvent.AddAnnotation(eventDef);
+ astEvent.Name = CleanName(eventDef.Name);
+ astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef);
+ if (eventDef.AddMethod == null || !IsExplicitInterfaceImplementation(eventDef.AddMethod))
+ astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod);
+ else
+ astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType);
+
+ if (eventDef.AddMethod != null) {
+ astEvent.AddAccessor = new Accessor {
+ Body = CreateMethodBody(eventDef.AddMethod)
+ }.WithAnnotation(eventDef.AddMethod);
+ ConvertAttributes(astEvent.AddAccessor, eventDef.AddMethod);
+ }
+ if (eventDef.RemoveMethod != null) {
+ astEvent.RemoveAccessor = new Accessor {
+ Body = CreateMethodBody(eventDef.RemoveMethod)
+ }.WithAnnotation(eventDef.RemoveMethod);
+ ConvertAttributes(astEvent.RemoveAccessor, eventDef.RemoveMethod);
+ }
+ MethodDefinition accessor = eventDef.AddMethod ?? eventDef.RemoveMethod;
+ if (accessor.IsVirtual == accessor.IsNewSlot) {
+ SetNewModifier(astEvent);
+ }
+ return astEvent;
+ }
+ }
+
+ public bool DecompileMethodBodies { get; set; }
+
+ BlockStatement CreateMethodBody(MethodDefinition method, IEnumerable<ParameterDeclaration> parameters = null)
+ {
+ if (DecompileMethodBodies)
+ return AstMethodBodyBuilder.CreateMethodBody(method, context, parameters);
+ else
+ return null;
+ }
+
+ FieldDeclaration CreateField(FieldDefinition fieldDef)
+ {
+ FieldDeclaration astField = new FieldDeclaration();
+ astField.AddAnnotation(fieldDef);
+ VariableInitializer initializer = new VariableInitializer(CleanName(fieldDef.Name));
+ astField.AddChild(initializer, Roles.Variable);
+ astField.ReturnType = ConvertType(fieldDef.FieldType, fieldDef);
+ astField.Modifiers = ConvertModifiers(fieldDef);
+ if (fieldDef.HasConstant) {
+ initializer.Initializer = CreateExpressionForConstant(fieldDef.Constant, fieldDef.FieldType, fieldDef.DeclaringType.IsEnum);
+ }
+ ConvertAttributes(astField, fieldDef);
+ SetNewModifier(astField);
+ return astField;
+ }
+
+ static Expression CreateExpressionForConstant(object constant, TypeReference type, bool isEnumMemberDeclaration = false)
+ {
+ if (constant == null) {
+ if (type.IsValueType && !(type.Namespace == "System" && type.Name == "Nullable`1"))
+ return new DefaultValueExpression(ConvertType(type));
+ else
+ return new NullReferenceExpression();
+ } else {
+ TypeCode c = Type.GetTypeCode(constant.GetType());
+ if (c >= TypeCode.SByte && c <= TypeCode.UInt64 && !isEnumMemberDeclaration) {
+ return MakePrimitive((long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constant, false), type);
+ } else {
+ return new PrimitiveExpression(constant);
+ }
+ }
+ }
+
+ public static IEnumerable<ParameterDeclaration> MakeParameters(MethodDefinition method, bool isLambda = false)
+ {
+ var parameters = MakeParameters(method.Parameters, isLambda);
+ if (method.CallingConvention == MethodCallingConvention.VarArg) {
+ return parameters.Concat(new[] { new ParameterDeclaration { Type = new PrimitiveType("__arglist") } });
+ } else {
+ return parameters;
+ }
+ }
+
+ public static IEnumerable<ParameterDeclaration> MakeParameters(IEnumerable<ParameterDefinition> paramCol, bool isLambda = false)
+ {
+ foreach(ParameterDefinition paramDef in paramCol) {
+ ParameterDeclaration astParam = new ParameterDeclaration();
+ astParam.AddAnnotation(paramDef);
+ if (!(isLambda && paramDef.ParameterType.ContainsAnonymousType()))
+ astParam.Type = ConvertType(paramDef.ParameterType, paramDef);
+ astParam.Name = paramDef.Name;
+
+ if (paramDef.ParameterType is ByReferenceType) {
+ astParam.ParameterModifier = (!paramDef.IsIn && paramDef.IsOut) ? ParameterModifier.Out : ParameterModifier.Ref;
+ ComposedType ct = astParam.Type as ComposedType;
+ if (ct != null && ct.PointerRank > 0)
+ ct.PointerRank--;
+ }
+
+ if (paramDef.HasCustomAttributes) {
+ foreach (CustomAttribute ca in paramDef.CustomAttributes) {
+ if (ca.AttributeType.Name == "ParamArrayAttribute" && ca.AttributeType.Namespace == "System")
+ astParam.ParameterModifier = ParameterModifier.Params;
+ }
+ }
+ if (paramDef.IsOptional) {
+ astParam.DefaultExpression = CreateExpressionForConstant(paramDef.Constant, paramDef.ParameterType);
+ }
+
+ ConvertCustomAttributes(astParam, paramDef);
+ ModuleDefinition module = ((MethodDefinition)paramDef.Method).Module;
+ if (paramDef.HasMarshalInfo) {
+ astParam.Attributes.Add(new AttributeSection(ConvertMarshalInfo(paramDef, module)));
+ }
+ if (astParam.ParameterModifier != ParameterModifier.Out) {
+ if (paramDef.IsIn)
+ astParam.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(InAttribute), module)));
+ if (paramDef.IsOut)
+ astParam.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(OutAttribute), module)));
+ }
+ yield return astParam;
+ }
+ }
+
+ #region ConvertAttributes
+ void ConvertAttributes(EntityDeclaration attributedNode, TypeDefinition typeDefinition)
+ {
+ ConvertCustomAttributes(attributedNode, typeDefinition);
+ ConvertSecurityAttributes(attributedNode, typeDefinition);
+
+ // Handle the non-custom attributes:
+ #region SerializableAttribute
+ if (typeDefinition.IsSerializable)
+ attributedNode.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(SerializableAttribute))));
+ #endregion
+
+ #region ComImportAttribute
+ if (typeDefinition.IsImport)
+ attributedNode.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(ComImportAttribute))));
+ #endregion
+
+ #region StructLayoutAttribute
+ LayoutKind layoutKind = LayoutKind.Auto;
+ switch (typeDefinition.Attributes & TypeAttributes.LayoutMask) {
+ case TypeAttributes.SequentialLayout:
+ layoutKind = LayoutKind.Sequential;
+ break;
+ case TypeAttributes.ExplicitLayout:
+ layoutKind = LayoutKind.Explicit;
+ break;
+ }
+ CharSet charSet = CharSet.None;
+ switch (typeDefinition.Attributes & TypeAttributes.StringFormatMask) {
+ case TypeAttributes.AnsiClass:
+ charSet = CharSet.Ansi;
+ break;
+ case TypeAttributes.AutoClass:
+ charSet = CharSet.Auto;
+ break;
+ case TypeAttributes.UnicodeClass:
+ charSet = CharSet.Unicode;
+ break;
+ }
+ LayoutKind defaultLayoutKind = (typeDefinition.IsValueType && !typeDefinition.IsEnum) ? LayoutKind.Sequential: LayoutKind.Auto;
+ if (layoutKind != defaultLayoutKind || charSet != CharSet.Ansi || typeDefinition.PackingSize > 0 || typeDefinition.ClassSize > 0) {
+ var structLayout = CreateNonCustomAttribute(typeof(StructLayoutAttribute));
+ structLayout.Arguments.Add(new IdentifierExpression("LayoutKind").Member(layoutKind.ToString()));
+ if (charSet != CharSet.Ansi) {
+ structLayout.AddNamedArgument("CharSet", new IdentifierExpression("CharSet").Member(charSet.ToString()));
+ }
+ if (typeDefinition.PackingSize > 0) {
+ structLayout.AddNamedArgument("Pack", new PrimitiveExpression((int)typeDefinition.PackingSize));
+ }
+ if (typeDefinition.ClassSize > 0) {
+ structLayout.AddNamedArgument("Size", new PrimitiveExpression((int)typeDefinition.ClassSize));
+ }
+ attributedNode.Attributes.Add(new AttributeSection(structLayout));
+ }
+ #endregion
+ }
+
+ void ConvertAttributes(EntityDeclaration attributedNode, MethodDefinition methodDefinition)
+ {
+ ConvertCustomAttributes(attributedNode, methodDefinition);
+ ConvertSecurityAttributes(attributedNode, methodDefinition);
+
+ MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask;
+
+ #region DllImportAttribute
+ if (methodDefinition.HasPInvokeInfo && methodDefinition.PInvokeInfo != null) {
+ PInvokeInfo info = methodDefinition.PInvokeInfo;
+ Ast.Attribute dllImport = CreateNonCustomAttribute(typeof(DllImportAttribute));
+ dllImport.Arguments.Add(new PrimitiveExpression(info.Module.Name));
+
+ if (info.IsBestFitDisabled)
+ dllImport.AddNamedArgument("BestFitMapping", new PrimitiveExpression(false));
+ if (info.IsBestFitEnabled)
+ dllImport.AddNamedArgument("BestFitMapping", new PrimitiveExpression(true));
+
+ CallingConvention callingConvention;
+ switch (info.Attributes & PInvokeAttributes.CallConvMask) {
+ case PInvokeAttributes.CallConvCdecl:
+ callingConvention = CallingConvention.Cdecl;
+ break;
+ case PInvokeAttributes.CallConvFastcall:
+ callingConvention = CallingConvention.FastCall;
+ break;
+ case PInvokeAttributes.CallConvStdCall:
+ callingConvention = CallingConvention.StdCall;
+ break;
+ case PInvokeAttributes.CallConvThiscall:
+ callingConvention = CallingConvention.ThisCall;
+ break;
+ case PInvokeAttributes.CallConvWinapi:
+ callingConvention = CallingConvention.Winapi;
+ break;
+ default:
+ throw new NotSupportedException("unknown calling convention");
+ }
+ if (callingConvention != CallingConvention.Winapi)
+ dllImport.AddNamedArgument("CallingConvention", new IdentifierExpression("CallingConvention").Member(callingConvention.ToString()));
+
+ CharSet charSet = CharSet.None;
+ switch (info.Attributes & PInvokeAttributes.CharSetMask) {
+ case PInvokeAttributes.CharSetAnsi:
+ charSet = CharSet.Ansi;
+ break;
+ case PInvokeAttributes.CharSetAuto:
+ charSet = CharSet.Auto;
+ break;
+ case PInvokeAttributes.CharSetUnicode:
+ charSet = CharSet.Unicode;
+ break;
+ }
+ if (charSet != CharSet.None)
+ dllImport.AddNamedArgument("CharSet", new IdentifierExpression("CharSet").Member(charSet.ToString()));
+
+ if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != methodDefinition.Name)
+ dllImport.AddNamedArgument("EntryPoint", new PrimitiveExpression(info.EntryPoint));
+
+ if (info.IsNoMangle)
+ dllImport.AddNamedArgument("ExactSpelling", new PrimitiveExpression(true));
+
+ if ((implAttributes & MethodImplAttributes.PreserveSig) == MethodImplAttributes.PreserveSig)
+ implAttributes &= ~MethodImplAttributes.PreserveSig;
+ else
+ dllImport.AddNamedArgument("PreserveSig", new PrimitiveExpression(false));
+
+ if (info.SupportsLastError)
+ dllImport.AddNamedArgument("SetLastError", new PrimitiveExpression(true));
+
+ if (info.IsThrowOnUnmappableCharDisabled)
+ dllImport.AddNamedArgument("ThrowOnUnmappableChar", new PrimitiveExpression(false));
+ if (info.IsThrowOnUnmappableCharEnabled)
+ dllImport.AddNamedArgument("ThrowOnUnmappableChar", new PrimitiveExpression(true));
+
+ attributedNode.Attributes.Add(new AttributeSection(dllImport));
+ }
+ #endregion
+
+ #region PreserveSigAttribute
+ if (implAttributes == MethodImplAttributes.PreserveSig) {
+ attributedNode.Attributes.Add(new AttributeSection(CreateNonCustomAttribute(typeof(PreserveSigAttribute))));
+ implAttributes = 0;
+ }
+ #endregion
+
+ #region MethodImplAttribute
+ if (implAttributes != 0) {
+ Ast.Attribute methodImpl = CreateNonCustomAttribute(typeof(MethodImplAttribute));
+ TypeReference methodImplOptions = new TypeReference(
+ "System.Runtime.CompilerServices", "MethodImplOptions",
+ methodDefinition.Module, methodDefinition.Module.TypeSystem.Corlib);
+ methodImpl.Arguments.Add(MakePrimitive((long)implAttributes, methodImplOptions));
+ attributedNode.Attributes.Add(new AttributeSection(methodImpl));
+ }
+ #endregion
+
+ ConvertAttributes(attributedNode, methodDefinition.MethodReturnType, methodDefinition.Module);
+ }
+
+ void ConvertAttributes(EntityDeclaration attributedNode, MethodReturnType methodReturnType, ModuleDefinition module)
+ {
+ ConvertCustomAttributes(attributedNode, methodReturnType, "return");
+ if (methodReturnType.HasMarshalInfo) {
+ var marshalInfo = ConvertMarshalInfo(methodReturnType, module);
+ attributedNode.Attributes.Add(new AttributeSection(marshalInfo) { AttributeTarget = "return" });
+ }
+ }
+
+ internal static void ConvertAttributes(EntityDeclaration attributedNode, FieldDefinition fieldDefinition, string attributeTarget = null)
+ {
+ ConvertCustomAttributes(attributedNode, fieldDefinition);
+
+ #region FieldOffsetAttribute
+ if (fieldDefinition.HasLayoutInfo) {
+ Ast.Attribute fieldOffset = CreateNonCustomAttribute(typeof(FieldOffsetAttribute), fieldDefinition.Module);
+ fieldOffset.Arguments.Add(new PrimitiveExpression(fieldDefinition.Offset));
+ attributedNode.Attributes.Add(new AttributeSection(fieldOffset) { AttributeTarget = attributeTarget });
+ }
+ #endregion
+
+ #region NonSerializedAttribute
+ if (fieldDefinition.IsNotSerialized) {
+ Ast.Attribute nonSerialized = CreateNonCustomAttribute(typeof(NonSerializedAttribute), fieldDefinition.Module);
+ attributedNode.Attributes.Add(new AttributeSection(nonSerialized) { AttributeTarget = attributeTarget });
+ }
+ #endregion
+
+ if (fieldDefinition.HasMarshalInfo) {
+ attributedNode.Attributes.Add(new AttributeSection(ConvertMarshalInfo(fieldDefinition, fieldDefinition.Module)) { AttributeTarget = attributeTarget });
+ }
+ }
+
+ #region MarshalAsAttribute (ConvertMarshalInfo)
+ static Ast.Attribute ConvertMarshalInfo(IMarshalInfoProvider marshalInfoProvider, ModuleDefinition module)
+ {
+ MarshalInfo marshalInfo = marshalInfoProvider.MarshalInfo;
+ Ast.Attribute attr = CreateNonCustomAttribute(typeof(MarshalAsAttribute), module);
+ var unmanagedType = new TypeReference("System.Runtime.InteropServices", "UnmanagedType", module, module.TypeSystem.Corlib);
+ attr.Arguments.Add(MakePrimitive((int)marshalInfo.NativeType, unmanagedType));
+
+ FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo;
+ if (fami != null) {
+ attr.AddNamedArgument("SizeConst", new PrimitiveExpression(fami.Size));
+ if (fami.ElementType != NativeType.None)
+ attr.AddNamedArgument("ArraySubType", MakePrimitive((int)fami.ElementType, unmanagedType));
+ }
+ SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo;
+ if (sami != null && sami.ElementType != VariantType.None) {
+ var varEnum = new TypeReference("System.Runtime.InteropServices", "VarEnum", module, module.TypeSystem.Corlib);
+ attr.AddNamedArgument("SafeArraySubType", MakePrimitive((int)sami.ElementType, varEnum));
+ }
+ ArrayMarshalInfo ami = marshalInfo as ArrayMarshalInfo;
+ if (ami != null) {
+ if (ami.ElementType != NativeType.Max)
+ attr.AddNamedArgument("ArraySubType", MakePrimitive((int)ami.ElementType, unmanagedType));
+ if (ami.Size >= 0)
+ attr.AddNamedArgument("SizeConst", new PrimitiveExpression(ami.Size));
+ if (ami.SizeParameterMultiplier != 0 && ami.SizeParameterIndex >= 0)
+ attr.AddNamedArgument("SizeParamIndex", new PrimitiveExpression(ami.SizeParameterIndex));
+ }
+ CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo;
+ if (cmi != null) {
+ attr.AddNamedArgument("MarshalType", new PrimitiveExpression(cmi.ManagedType.FullName));
+ if (!string.IsNullOrEmpty(cmi.Cookie))
+ attr.AddNamedArgument("MarshalCookie", new PrimitiveExpression(cmi.Cookie));
+ }
+ FixedSysStringMarshalInfo fssmi = marshalInfo as FixedSysStringMarshalInfo;
+ if (fssmi != null) {
+ attr.AddNamedArgument("SizeConst", new PrimitiveExpression(fssmi.Size));
+ }
+ return attr;
+ }
+ #endregion
+
+ Ast.Attribute CreateNonCustomAttribute(Type attributeType)
+ {
+ return CreateNonCustomAttribute(attributeType, context.CurrentType != null ? context.CurrentType.Module : null);
+ }
+
+ static Ast.Attribute CreateNonCustomAttribute(Type attributeType, ModuleDefinition module)
+ {
+ Debug.Assert(attributeType.Name.EndsWith("Attribute", StringComparison.Ordinal));
+ Ast.Attribute attr = new Ast.Attribute();
+ attr.Type = new SimpleType(attributeType.Name.Substring(0, attributeType.Name.Length - "Attribute".Length));
+ if (module != null) {
+ attr.Type.AddAnnotation(new TypeReference(attributeType.Namespace, attributeType.Name, module, module.TypeSystem.Corlib));
+ }
+ return attr;
+ }
+
+ static void ConvertCustomAttributes(AstNode attributedNode, ICustomAttributeProvider customAttributeProvider, string attributeTarget = null)
+ {
+ EntityDeclaration entityDecl = attributedNode as EntityDeclaration;
+ if (customAttributeProvider.HasCustomAttributes) {
+ var attributes = new List<NRefactory.CSharp.Attribute>();
+ foreach (var customAttribute in customAttributeProvider.CustomAttributes.OrderBy(a => a.AttributeType.FullName)) {
+ if (customAttribute.AttributeType.Name == "ExtensionAttribute" && customAttribute.AttributeType.Namespace == "System.Runtime.CompilerServices") {
+ // don't show the ExtensionAttribute (it's converted to the 'this' modifier)
+ continue;
+ }
+ if (customAttribute.AttributeType.Name == "ParamArrayAttribute" && customAttribute.AttributeType.Namespace == "System") {
+ // don't show the ParamArrayAttribute (it's converted to the 'params' modifier)
+ continue;
+ }
+ // if the method is async, remove [DebuggerStepThrough] and [Async
+ if (entityDecl != null && entityDecl.HasModifier(Modifiers.Async)) {
+ if (customAttribute.AttributeType.Name == "DebuggerStepThroughAttribute" && customAttribute.AttributeType.Namespace == "System.Diagnostics") {
+ continue;
+ }
+ if (customAttribute.AttributeType.Name == "AsyncStateMachineAttribute" && customAttribute.AttributeType.Namespace == "System.Runtime.CompilerServices") {
+ continue;
+ }
+ }
+
+ var attribute = new NRefactory.CSharp.Attribute();
+ attribute.AddAnnotation(customAttribute);
+ attribute.Type = ConvertType(customAttribute.AttributeType);
+ attributes.Add(attribute);
+
+ SimpleType st = attribute.Type as SimpleType;
+ if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) {
+ st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - "Attribute".Length);
+ }
+
+ if(customAttribute.HasConstructorArguments) {
+ foreach (var parameter in customAttribute.ConstructorArguments) {
+ Expression parameterValue = ConvertArgumentValue(parameter);
+ attribute.Arguments.Add(parameterValue);
+ }
+ }
+ if (customAttribute.HasProperties) {
+ TypeDefinition resolvedAttributeType = customAttribute.AttributeType.Resolve();
+ foreach (var propertyNamedArg in customAttribute.Properties) {
+ var propertyReference = resolvedAttributeType != null ? resolvedAttributeType.Properties.FirstOrDefault(pr => pr.Name == propertyNamedArg.Name) : null;
+ var propertyName = new IdentifierExpression(propertyNamedArg.Name).WithAnnotation(propertyReference);
+ var argumentValue = ConvertArgumentValue(propertyNamedArg.Argument);
+ attribute.Arguments.Add(new AssignmentExpression(propertyName, argumentValue));
+ }
+ }
+
+ if (customAttribute.HasFields) {
+ TypeDefinition resolvedAttributeType = customAttribute.AttributeType.Resolve();
+ foreach (var fieldNamedArg in customAttribute.Fields) {
+ var fieldReference = resolvedAttributeType != null ? resolvedAttributeType.Fields.FirstOrDefault(f => f.Name == fieldNamedArg.Name) : null;
+ var fieldName = new IdentifierExpression(fieldNamedArg.Name).WithAnnotation(fieldReference);
+ var argumentValue = ConvertArgumentValue(fieldNamedArg.Argument);
+ attribute.Arguments.Add(new AssignmentExpression(fieldName, argumentValue));
+ }
+ }
+ }
+
+ if (attributeTarget == "module" || attributeTarget == "assembly") {
+ // use separate section for each attribute
+ foreach (var attribute in attributes) {
+ var section = new AttributeSection();
+ section.AttributeTarget = attributeTarget;
+ section.Attributes.Add(attribute);
+ attributedNode.AddChild(section, EntityDeclaration.AttributeRole);
+ }
+ } else if (attributes.Count > 0) {
+ // use single section for all attributes
+ var section = new AttributeSection();
+ section.AttributeTarget = attributeTarget;
+ section.Attributes.AddRange(attributes);
+ attributedNode.AddChild(section, EntityDeclaration.AttributeRole);
+ }
+ }
+ }
+
+ static void ConvertSecurityAttributes(AstNode attributedNode, ISecurityDeclarationProvider secDeclProvider, string attributeTarget = null)
+ {
+ if (!secDeclProvider.HasSecurityDeclarations)
+ return;
+ var attributes = new List<NRefactory.CSharp.Attribute>();
+ foreach (var secDecl in secDeclProvider.SecurityDeclarations.OrderBy(d => d.Action)) {
+ foreach (var secAttribute in secDecl.SecurityAttributes.OrderBy(a => a.AttributeType.FullName)) {
+ var attribute = new NRefactory.CSharp.Attribute();
+ attribute.AddAnnotation(secAttribute);
+ attribute.Type = ConvertType(secAttribute.AttributeType);
+ attributes.Add(attribute);
+
+ SimpleType st = attribute.Type as SimpleType;
+ if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) {
+ st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - "Attribute".Length);
+ }
+
+ var module = secAttribute.AttributeType.Module;
+ var securityActionType = new TypeReference("System.Security.Permissions", "SecurityAction", module, module.TypeSystem.Corlib);
+ attribute.Arguments.Add(MakePrimitive((int)secDecl.Action, securityActionType));
+
+ if (secAttribute.HasProperties) {
+ TypeDefinition resolvedAttributeType = secAttribute.AttributeType.Resolve();
+ foreach (var propertyNamedArg in secAttribute.Properties) {
+ var propertyReference = resolvedAttributeType != null ? resolvedAttributeType.Properties.FirstOrDefault(pr => pr.Name == propertyNamedArg.Name) : null;
+ var propertyName = new IdentifierExpression(propertyNamedArg.Name).WithAnnotation(propertyReference);
+ var argumentValue = ConvertArgumentValue(propertyNamedArg.Argument);
+ attribute.Arguments.Add(new AssignmentExpression(propertyName, argumentValue));
+ }
+ }
+
+ if (secAttribute.HasFields) {
+ TypeDefinition resolvedAttributeType = secAttribute.AttributeType.Resolve();
+ foreach (var fieldNamedArg in secAttribute.Fields) {
+ var fieldReference = resolvedAttributeType != null ? resolvedAttributeType.Fields.FirstOrDefault(f => f.Name == fieldNamedArg.Name) : null;
+ var fieldName = new IdentifierExpression(fieldNamedArg.Name).WithAnnotation(fieldReference);
+ var argumentValue = ConvertArgumentValue(fieldNamedArg.Argument);
+ attribute.Arguments.Add(new AssignmentExpression(fieldName, argumentValue));
+ }
+ }
+ }
+ }
+ if (attributeTarget == "module" || attributeTarget == "assembly") {
+ // use separate section for each attribute
+ foreach (var attribute in attributes) {
+ var section = new AttributeSection();
+ section.AttributeTarget = attributeTarget;
+ section.Attributes.Add(attribute);
+ attributedNode.AddChild(section, EntityDeclaration.AttributeRole);
+ }
+ } else if (attributes.Count > 0) {
+ // use single section for all attributes
+ var section = new AttributeSection();
+ section.AttributeTarget = attributeTarget;
+ section.Attributes.AddRange(attributes);
+ attributedNode.AddChild(section, EntityDeclaration.AttributeRole);
+ }
+ }
+
+ static Expression ConvertArgumentValue(CustomAttributeArgument argument)
+ {
+ if (argument.Value is CustomAttributeArgument[]) {
+ ArrayInitializerExpression arrayInit = new ArrayInitializerExpression();
+ foreach (CustomAttributeArgument element in (CustomAttributeArgument[])argument.Value) {
+ arrayInit.Elements.Add(ConvertArgumentValue(element));
+ }
+ ArrayType arrayType = argument.Type as ArrayType;
+ return new ArrayCreateExpression {
+ Type = ConvertType(arrayType != null ? arrayType.ElementType : argument.Type),
+ AdditionalArraySpecifiers = { new ArraySpecifier() },
+ Initializer = arrayInit
+ };
+ } else if (argument.Value is CustomAttributeArgument) {
+ // occurs with boxed arguments
+ return ConvertArgumentValue((CustomAttributeArgument)argument.Value);
+ }
+ var type = argument.Type.Resolve();
+ if (type != null && type.IsEnum) {
+ return MakePrimitive(Convert.ToInt64(argument.Value), type);
+ } else if (argument.Value is TypeReference) {
+ return CreateTypeOfExpression((TypeReference)argument.Value);
+ } else {
+ return new PrimitiveExpression(argument.Value);
+ }
+ }
+ #endregion
+
+ internal static Expression MakePrimitive(long val, TypeReference type)
+ {
+ if (TypeAnalysis.IsBoolean(type) && val == 0)
+ return new PrimitiveExpression(false);
+ else if (TypeAnalysis.IsBoolean(type) && val == 1)
+ return new PrimitiveExpression(true);
+ else if (val == 0 && type is PointerType)
+ return new NullReferenceExpression();
+ if (type != null)
+ { // cannot rely on type.IsValueType, it's not set for typerefs (but is set for typespecs)
+ TypeDefinition enumDefinition = type.Resolve();
+ if (enumDefinition != null && enumDefinition.IsEnum) {
+ TypeCode enumBaseTypeCode = TypeCode.Int32;
+ foreach (FieldDefinition field in enumDefinition.Fields) {
+ if (field.IsStatic && Equals(CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false), val))
+ return ConvertType(type).Member(field.Name).WithAnnotation(field);
+ else if (!field.IsStatic)
+ enumBaseTypeCode = TypeAnalysis.GetTypeCode(field.FieldType); // use primitive type of the enum
+ }
+ if (IsFlagsEnum(enumDefinition)) {
+ long enumValue = val;
+ Expression expr = null;
+ long negatedEnumValue = ~val;
+ // limit negatedEnumValue to the appropriate range
+ switch (enumBaseTypeCode) {
+ case TypeCode.Byte:
+ case TypeCode.SByte:
+ negatedEnumValue &= byte.MaxValue;
+ break;
+ case TypeCode.Int16:
+ case TypeCode.UInt16:
+ negatedEnumValue &= ushort.MaxValue;
+ break;
+ case TypeCode.Int32:
+ case TypeCode.UInt32:
+ negatedEnumValue &= uint.MaxValue;
+ break;
+ }
+ Expression negatedExpr = null;
+ foreach (FieldDefinition field in enumDefinition.Fields.Where(fld => fld.IsStatic)) {
+ long fieldValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false);
+ if (fieldValue == 0)
+ continue; // skip None enum value
+
+ if ((fieldValue & enumValue) == fieldValue) {
+ var fieldExpression = ConvertType(type).Member(field.Name).WithAnnotation(field);
+ if (expr == null)
+ expr = fieldExpression;
+ else
+ expr = new BinaryOperatorExpression(expr, BinaryOperatorType.BitwiseOr, fieldExpression);
+
+ enumValue &= ~fieldValue;
+ }
+ if ((fieldValue & negatedEnumValue) == fieldValue) {
+ var fieldExpression = ConvertType(type).Member(field.Name).WithAnnotation(field);
+ if (negatedExpr == null)
+ negatedExpr = fieldExpression;
+ else
+ negatedExpr = new BinaryOperatorExpression(negatedExpr, BinaryOperatorType.BitwiseOr, fieldExpression);
+
+ negatedEnumValue &= ~fieldValue;
+ }
+ }
+ if (enumValue == 0 && expr != null) {
+ if (!(negatedEnumValue == 0 && negatedExpr != null && negatedExpr.Descendants.Count() < expr.Descendants.Count())) {
+ return expr;
+ }
+ }
+ if (negatedEnumValue == 0 && negatedExpr != null) {
+ return new UnaryOperatorExpression(UnaryOperatorType.BitNot, negatedExpr);
+ }
+ }
+ return new PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false)).CastTo(ConvertType(type));
+ }
+ }
+ TypeCode code = TypeAnalysis.GetTypeCode(type);
+ if (code == TypeCode.Object || code == TypeCode.Empty)
+ code = TypeCode.Int32;
+ return new PrimitiveExpression(CSharpPrimitiveCast.Cast(code, val, false));
+ }
+
+ static bool IsFlagsEnum(TypeDefinition type)
+ {
+ if (!type.HasCustomAttributes)
+ return false;
+
+ return type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.FlagsAttribute");
+ }
+
+ /// <summary>
+ /// Sets new modifier if the member hides some other member from a base type.
+ /// </summary>
+ /// <param name="member">The node of the member which new modifier state should be determined.</param>
+ static void SetNewModifier(EntityDeclaration member)
+ {
+ try {
+ bool addNewModifier = false;
+ if (member is IndexerDeclaration) {
+ var propertyDef = member.Annotation<PropertyDefinition>();
+ var baseProperties =
+ TypesHierarchyHelpers.FindBaseProperties(propertyDef);
+ addNewModifier = baseProperties.Any();
+ } else
+ addNewModifier = HidesBaseMember(member);
+
+ if (addNewModifier)
+ member.Modifiers |= Modifiers.New;
+ }
+ catch (ReferenceResolvingException) {
+ // TODO: add some kind of notification (a comment?) about possible problems with decompiled code due to unresolved references.
+ }
+ }
+
+ static bool HidesBaseMember(EntityDeclaration member)
+ {
+ var memberDefinition = member.Annotation<IMemberDefinition>();
+ bool addNewModifier = false;
+ var methodDefinition = memberDefinition as MethodDefinition;
+ if (methodDefinition != null) {
+ addNewModifier = HidesByName(memberDefinition, includeBaseMethods: false);
+ if (!addNewModifier)
+ addNewModifier = TypesHierarchyHelpers.FindBaseMethods(methodDefinition).Any();
+ } else
+ addNewModifier = HidesByName(memberDefinition, includeBaseMethods: true);
+ return addNewModifier;
+ }
+
+ /// <summary>
+ /// Determines whether any base class member has the same name as the given member.
+ /// </summary>
+ /// <param name="member">The derived type's member.</param>
+ /// <param name="includeBaseMethods">true if names of methods declared in base types should also be checked.</param>
+ /// <returns>true if any base member has the same name as given member, otherwise false.</returns>
+ static bool HidesByName(IMemberDefinition member, bool includeBaseMethods)
+ {
+ Debug.Assert(!(member is PropertyDefinition) || !((PropertyDefinition)member).IsIndexer());
+
+ if (member.DeclaringType.BaseType != null) {
+ var baseTypeRef = member.DeclaringType.BaseType;
+ while (baseTypeRef != null) {
+ var baseType = baseTypeRef.ResolveOrThrow();
+ if (baseType.HasProperties && AnyIsHiddenBy(baseType.Properties, member, m => !m.IsIndexer()))
+ return true;
+ if (baseType.HasEvents && AnyIsHiddenBy(baseType.Events, member))
+ return true;
+ if (baseType.HasFields && AnyIsHiddenBy(baseType.Fields, member))
+ return true;
+ if (includeBaseMethods && baseType.HasMethods
+ && AnyIsHiddenBy(baseType.Methods, member, m => !m.IsSpecialName))
+ return true;
+ if (baseType.HasNestedTypes && AnyIsHiddenBy(baseType.NestedTypes, member))
+ return true;
+ baseTypeRef = baseType.BaseType;
+ }
+ }
+ return false;
+ }
+
+ static bool AnyIsHiddenBy<T>(IEnumerable<T> members, IMemberDefinition derived, Predicate<T> condition = null)
+ where T : IMemberDefinition
+ {
+ return members.Any(m => m.Name == derived.Name
+ && (condition == null || condition(m))
+ && TypesHierarchyHelpers.IsVisibleFromDerived(m, derived.DeclaringType));
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
new file mode 100644
index 00000000..a4d80e59
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
@@ -0,0 +1,1220 @@
+// 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.Diagnostics;
+using System.Linq;
+using System.Threading;
+
+using ICSharpCode.Decompiler.Ast.Transforms;
+using ICSharpCode.Decompiler.ILAst;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.PatternMatching;
+using ICSharpCode.NRefactory.Utils;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.Ast
+{
+ using Ast = NRefactory.CSharp;
+ using Cecil = Mono.Cecil;
+
+ public class AstMethodBodyBuilder
+ {
+ MethodDefinition methodDef;
+ TypeSystem typeSystem;
+ DecompilerContext context;
+ HashSet<ILVariable> localVariablesToDefine = new HashSet<ILVariable>(); // local variables that are missing a definition
+
+ /// <summary>
+ /// Creates the body for the method definition.
+ /// </summary>
+ /// <param name="methodDef">Method definition to decompile.</param>
+ /// <param name="context">Decompilation context.</param>
+ /// <param name="parameters">Parameter declarations of the method being decompiled.
+ /// These are used to update the parameter names when the decompiler generates names for the parameters.</param>
+ /// <returns>Block for the method body</returns>
+ public static BlockStatement CreateMethodBody(MethodDefinition methodDef,
+ DecompilerContext context,
+ IEnumerable<ParameterDeclaration> parameters = null)
+ {
+ MethodDefinition oldCurrentMethod = context.CurrentMethod;
+ Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef);
+ context.CurrentMethod = methodDef;
+ context.CurrentMethodIsAsync = false;
+ try {
+ AstMethodBodyBuilder builder = new AstMethodBodyBuilder();
+ builder.methodDef = methodDef;
+ builder.context = context;
+ builder.typeSystem = methodDef.Module.TypeSystem;
+ if (Debugger.IsAttached) {
+ return builder.CreateMethodBody(parameters);
+ } else {
+ try {
+ return builder.CreateMethodBody(parameters);
+ } catch (OperationCanceledException) {
+ throw;
+ } catch (Exception ex) {
+ throw new DecompilerException(methodDef, ex);
+ }
+ }
+ } finally {
+ context.CurrentMethod = oldCurrentMethod;
+ }
+ }
+
+ public BlockStatement CreateMethodBody(IEnumerable<ParameterDeclaration> parameters)
+ {
+ if (methodDef.Body == null) {
+ return null;
+ }
+
+ context.CancellationToken.ThrowIfCancellationRequested();
+ ILBlock ilMethod = new ILBlock();
+ ILAstBuilder astBuilder = new ILAstBuilder();
+ ilMethod.Body = astBuilder.Build(methodDef, true, context);
+
+ context.CancellationToken.ThrowIfCancellationRequested();
+ ILAstOptimizer bodyGraph = new ILAstOptimizer();
+ bodyGraph.Optimize(context, ilMethod);
+ context.CancellationToken.ThrowIfCancellationRequested();
+
+ var localVariables = ilMethod.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable)
+ .Where(v => v != null && !v.IsParameter).Distinct();
+ Debug.Assert(context.CurrentMethod == methodDef);
+ NameVariables.AssignNamesToVariables(context, astBuilder.Parameters, localVariables, ilMethod);
+
+ if (parameters != null) {
+ foreach (var pair in (from p in parameters
+ join v in astBuilder.Parameters on p.Annotation<ParameterDefinition>() equals v.OriginalParameter
+ select new { p, v.Name }))
+ {
+ pair.p.Name = pair.Name;
+ }
+ }
+
+ context.CancellationToken.ThrowIfCancellationRequested();
+ BlockStatement astBlock = TransformBlock(ilMethod);
+ CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments
+
+ Statement insertionPoint = astBlock.Statements.FirstOrDefault();
+ foreach (ILVariable v in localVariablesToDefine) {
+ AstType type;
+ if (v.Type.ContainsAnonymousType())
+ type = new SimpleType("var");
+ else
+ type = AstBuilder.ConvertType(v.Type);
+ var newVarDecl = new VariableDeclarationStatement(type, v.Name);
+ newVarDecl.Variables.Single().AddAnnotation(v);
+ astBlock.Statements.InsertBefore(insertionPoint, newVarDecl);
+ }
+
+ astBlock.AddAnnotation(new MethodDebugSymbols(methodDef) { LocalVariables = localVariables.ToList() });
+
+ return astBlock;
+ }
+
+ BlockStatement TransformBlock(ILBlock block)
+ {
+ BlockStatement astBlock = new BlockStatement();
+ if (block != null) {
+ foreach(ILNode node in block.GetChildren()) {
+ astBlock.Statements.AddRange(TransformNode(node));
+ }
+ }
+ return astBlock;
+ }
+
+ IEnumerable<Statement> TransformNode(ILNode node)
+ {
+ if (node is ILLabel) {
+ yield return new LabelStatement { Label = ((ILLabel)node).Name };
+ } else if (node is ILExpression) {
+ List<ILRange> ilRanges = ILRange.OrderAndJoin(node.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.ILRanges));
+ AstNode codeExpr = TransformExpression((ILExpression)node);
+ if (codeExpr != null) {
+ codeExpr = codeExpr.WithAnnotation(ilRanges);
+ if (codeExpr is Expression) {
+ yield return new ExpressionStatement { Expression = (Expression)codeExpr };
+ } else if (codeExpr is Statement) {
+ yield return (Statement)codeExpr;
+ } else {
+ throw new Exception();
+ }
+ }
+ } else if (node is ILWhileLoop) {
+ ILWhileLoop ilLoop = (ILWhileLoop)node;
+ WhileStatement whileStmt = new WhileStatement() {
+ Condition = ilLoop.Condition != null ? (Expression)TransformExpression(ilLoop.Condition) : new PrimitiveExpression(true),
+ EmbeddedStatement = TransformBlock(ilLoop.BodyBlock)
+ };
+ yield return whileStmt;
+ } else if (node is ILCondition) {
+ ILCondition conditionalNode = (ILCondition)node;
+ bool hasFalseBlock = conditionalNode.FalseBlock.EntryGoto != null || conditionalNode.FalseBlock.Body.Count > 0;
+ yield return new IfElseStatement {
+ Condition = (Expression)TransformExpression(conditionalNode.Condition),
+ TrueStatement = TransformBlock(conditionalNode.TrueBlock),
+ FalseStatement = hasFalseBlock ? TransformBlock(conditionalNode.FalseBlock) : null
+ };
+ } else if (node is ILSwitch) {
+ ILSwitch ilSwitch = (ILSwitch)node;
+ if (TypeAnalysis.IsBoolean(ilSwitch.Condition.InferredType) && (
+ from cb in ilSwitch.CaseBlocks
+ where cb.Values != null
+ from val in cb.Values
+ select val
+ ).Any(val => val != 0 && val != 1))
+ {
+ // If switch cases contain values other then 0 and 1, force the condition to be non-boolean
+ ilSwitch.Condition.ExpectedType = typeSystem.Int32;
+ }
+ SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition) };
+ foreach (var caseBlock in ilSwitch.CaseBlocks) {
+ SwitchSection section = new SwitchSection();
+ if (caseBlock.Values != null) {
+ section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = AstBuilder.MakePrimitive(i, ilSwitch.Condition.ExpectedType ?? ilSwitch.Condition.InferredType) }));
+ } else {
+ section.CaseLabels.Add(new CaseLabel());
+ }
+ section.Statements.Add(TransformBlock(caseBlock));
+ switchStmt.SwitchSections.Add(section);
+ }
+ yield return switchStmt;
+ } else if (node is ILTryCatchBlock) {
+ ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node);
+ var tryCatchStmt = new TryCatchStatement();
+ tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock);
+ foreach (var catchClause in tryCatchNode.CatchBlocks) {
+ if (catchClause.ExceptionVariable == null
+ && (catchClause.ExceptionType == null || catchClause.ExceptionType.MetadataType == MetadataType.Object))
+ {
+ tryCatchStmt.CatchClauses.Add(new CatchClause { Body = TransformBlock(catchClause) });
+ } else {
+ tryCatchStmt.CatchClauses.Add(
+ new CatchClause {
+ Type = AstBuilder.ConvertType(catchClause.ExceptionType),
+ VariableName = catchClause.ExceptionVariable == null ? null : catchClause.ExceptionVariable.Name,
+ Body = TransformBlock(catchClause)
+ }.WithAnnotation(catchClause.ExceptionVariable));
+ }
+ }
+ if (tryCatchNode.FinallyBlock != null)
+ tryCatchStmt.FinallyBlock = TransformBlock(tryCatchNode.FinallyBlock);
+ if (tryCatchNode.FaultBlock != null) {
+ CatchClause cc = new CatchClause();
+ cc.Body = TransformBlock(tryCatchNode.FaultBlock);
+ cc.Body.Add(new ThrowStatement()); // rethrow
+ tryCatchStmt.CatchClauses.Add(cc);
+ }
+ yield return tryCatchStmt;
+ } else if (node is ILFixedStatement) {
+ ILFixedStatement fixedNode = (ILFixedStatement)node;
+ FixedStatement fixedStatement = new FixedStatement();
+ foreach (ILExpression initializer in fixedNode.Initializers) {
+ Debug.Assert(initializer.Code == ILCode.Stloc);
+ ILVariable v = (ILVariable)initializer.Operand;
+ fixedStatement.Variables.Add(
+ new VariableInitializer {
+ Name = v.Name,
+ Initializer = (Expression)TransformExpression(initializer.Arguments[0])
+ }.WithAnnotation(v));
+ }
+ fixedStatement.Type = AstBuilder.ConvertType(((ILVariable)fixedNode.Initializers[0].Operand).Type);
+ fixedStatement.EmbeddedStatement = TransformBlock(fixedNode.BodyBlock);
+ yield return fixedStatement;
+ } else if (node is ILBlock) {
+ yield return TransformBlock((ILBlock)node);
+ } else {
+ throw new Exception("Unknown node type");
+ }
+ }
+
+ AstNode TransformExpression(ILExpression expr)
+ {
+ AstNode node = TransformByteCode(expr);
+ Expression astExpr = node as Expression;
+
+ // get IL ranges - used in debugger
+ List<ILRange> ilRanges = ILRange.OrderAndJoin(expr.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.ILRanges));
+ AstNode result;
+
+ if (astExpr != null)
+ result = Convert(astExpr, expr.InferredType, expr.ExpectedType);
+ else
+ result = node;
+
+ if (result != null)
+ result = result.WithAnnotation(new TypeInformation(expr.InferredType, expr.ExpectedType));
+
+ if (result != null)
+ return result.WithAnnotation(ilRanges);
+
+ return result;
+ }
+
+ AstNode TransformByteCode(ILExpression byteCode)
+ {
+ object operand = byteCode.Operand;
+ AstType operandAsTypeRef = AstBuilder.ConvertType(operand as TypeReference);
+
+ List<Expression> args = new List<Expression>();
+ foreach(ILExpression arg in byteCode.Arguments) {
+ args.Add((Expression)TransformExpression(arg));
+ }
+ Expression arg1 = args.Count >= 1 ? args[0] : null;
+ Expression arg2 = args.Count >= 2 ? args[1] : null;
+ Expression arg3 = args.Count >= 3 ? args[2] : null;
+
+ switch (byteCode.Code) {
+ #region Arithmetic
+ case ILCode.Add:
+ case ILCode.Add_Ovf:
+ case ILCode.Add_Ovf_Un:
+ {
+ BinaryOperatorExpression boe;
+ if (byteCode.InferredType is PointerType) {
+ boe = new BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
+ if (byteCode.Arguments[0].ExpectedType is PointerType ||
+ byteCode.Arguments[1].ExpectedType is PointerType) {
+ boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation);
+ }
+ } else {
+ boe = new BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
+ }
+ boe.AddAnnotation(byteCode.Code == ILCode.Add ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation);
+ return boe;
+ }
+ case ILCode.Sub:
+ case ILCode.Sub_Ovf:
+ case ILCode.Sub_Ovf_Un:
+ {
+ BinaryOperatorExpression boe;
+ if (byteCode.InferredType is PointerType) {
+ boe = new BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
+ if (byteCode.Arguments[0].ExpectedType is PointerType) {
+ boe.WithAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation);
+ }
+ } else {
+ boe = new BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
+ }
+ boe.AddAnnotation(byteCode.Code == ILCode.Sub ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation);
+ return boe;
+ }
+ case ILCode.Div: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2);
+ case ILCode.Div_Un: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2);
+ case ILCode.Mul: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation);
+ case ILCode.Mul_Ovf: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation);
+ case ILCode.Mul_Ovf_Un: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation);
+ case ILCode.Rem: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2);
+ case ILCode.Rem_Un: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2);
+ case ILCode.And: return new BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2);
+ case ILCode.Or: return new BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2);
+ case ILCode.Xor: return new BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2);
+ case ILCode.Shl: return new BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2);
+ case ILCode.Shr: return new BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2);
+ case ILCode.Shr_Un: return new BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2);
+ case ILCode.Neg: return new UnaryOperatorExpression(UnaryOperatorType.Minus, arg1).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation);
+ case ILCode.Not: return new UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1);
+ case ILCode.PostIncrement:
+ case ILCode.PostIncrement_Ovf:
+ case ILCode.PostIncrement_Ovf_Un:
+ {
+ if (arg1 is DirectionExpression)
+ arg1 = ((DirectionExpression)arg1).Expression.Detach();
+ var uoe = new UnaryOperatorExpression(
+ (int)byteCode.Operand > 0 ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement, arg1);
+ uoe.AddAnnotation((byteCode.Code == ILCode.PostIncrement) ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation);
+ return uoe;
+ }
+ #endregion
+ #region Arrays
+ case ILCode.Newarr: {
+ var ace = new ArrayCreateExpression();
+ ace.Type = operandAsTypeRef;
+ ComposedType ct = operandAsTypeRef as ComposedType;
+ if (ct != null) {
+ // change "new (int[,])[10] to new int[10][,]"
+ ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
+ }
+ if (byteCode.Code == ILCode.InitArray) {
+ ace.Initializer = new ArrayInitializerExpression();
+ ace.Initializer.Elements.AddRange(args);
+ } else {
+ ace.Arguments.Add(arg1);
+ }
+ return ace;
+ }
+ case ILCode.InitArray: {
+ var ace = new ArrayCreateExpression();
+ ace.Type = operandAsTypeRef;
+ ComposedType ct = operandAsTypeRef as ComposedType;
+ var arrayType = (ArrayType) operand;
+ if (ct != null)
+ {
+ // change "new (int[,])[10] to new int[10][,]"
+ ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
+ ace.Initializer = new ArrayInitializerExpression();
+ }
+ var newArgs = new List<Expression>();
+ foreach (var arrayDimension in arrayType.Dimensions.Skip(1).Reverse())
+ {
+ int length = (int)arrayDimension.UpperBound - (int)arrayDimension.LowerBound;
+ for (int j = 0; j < args.Count; j += length)
+ {
+ var child = new ArrayInitializerExpression();
+ child.Elements.AddRange(args.GetRange(j, length));
+ newArgs.Add(child);
+ }
+ var temp = args;
+ args = newArgs;
+ newArgs = temp;
+ newArgs.Clear();
+ }
+ ace.Initializer.Elements.AddRange(args);
+ return ace;
+ }
+ case ILCode.Ldlen: return arg1.Member("Length");
+ case ILCode.Ldelem_I:
+ case ILCode.Ldelem_I1:
+ case ILCode.Ldelem_I2:
+ case ILCode.Ldelem_I4:
+ case ILCode.Ldelem_I8:
+ case ILCode.Ldelem_U1:
+ case ILCode.Ldelem_U2:
+ case ILCode.Ldelem_U4:
+ case ILCode.Ldelem_R4:
+ case ILCode.Ldelem_R8:
+ case ILCode.Ldelem_Ref:
+ case ILCode.Ldelem_Any:
+ return arg1.Indexer(arg2);
+ case ILCode.Ldelema:
+ return MakeRef(arg1.Indexer(arg2));
+ case ILCode.Stelem_I:
+ case ILCode.Stelem_I1:
+ case ILCode.Stelem_I2:
+ case ILCode.Stelem_I4:
+ case ILCode.Stelem_I8:
+ case ILCode.Stelem_R4:
+ case ILCode.Stelem_R8:
+ case ILCode.Stelem_Ref:
+ case ILCode.Stelem_Any:
+ return new AssignmentExpression(arg1.Indexer(arg2), arg3);
+ case ILCode.CompoundAssignment:
+ {
+ CastExpression cast = arg1 as CastExpression;
+ var boe = cast != null ? (BinaryOperatorExpression)cast.Expression : arg1 as BinaryOperatorExpression;
+ // AssignmentExpression doesn't support overloaded operators so they have to be processed to BinaryOperatorExpression
+ if (boe == null) {
+ var tmp = new ParenthesizedExpression(arg1);
+ ReplaceMethodCallsWithOperators.ProcessInvocationExpression((InvocationExpression)arg1);
+ boe = (BinaryOperatorExpression)tmp.Expression;
+ }
+ var assignment = new AssignmentExpression {
+ Left = boe.Left.Detach(),
+ Operator = ReplaceMethodCallsWithOperators.GetAssignmentOperatorForBinaryOperator(boe.Operator),
+ Right = boe.Right.Detach()
+ }.CopyAnnotationsFrom(boe);
+ // We do not mark the resulting assignment as RestoreOriginalAssignOperatorAnnotation, because
+ // the operator cannot be translated back to the expanded form (as the left-hand expression
+ // would be evaluated twice, and might have side-effects)
+ if (cast != null) {
+ cast.Expression = assignment;
+ return cast;
+ } else {
+ return assignment;
+ }
+ }
+ #endregion
+ #region Comparison
+ case ILCode.Ceq: return new BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2);
+ case ILCode.Cne: return new BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2);
+ case ILCode.Cgt: return new BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2);
+ case ILCode.Cgt_Un: {
+ // can also mean Inequality, when used with object references
+ TypeReference arg1Type = byteCode.Arguments[0].InferredType;
+ if (arg1Type != null && !arg1Type.IsValueType) goto case ILCode.Cne;
+
+ // when comparing signed integral values using Cgt_Un with 0
+ // the Ast should actually contain InEquality since "(uint)a > 0u" is identical to "a != 0"
+ if (arg1Type.IsSignedIntegralType())
+ {
+ var p = arg2 as PrimitiveExpression;
+ if (p != null && p.Value.IsZero()) goto case ILCode.Cne;
+ }
+
+ goto case ILCode.Cgt;
+ }
+ case ILCode.Cle_Un: {
+ // can also mean Equality, when used with object references
+ TypeReference arg1Type = byteCode.Arguments[0].InferredType;
+ if (arg1Type != null && !arg1Type.IsValueType) goto case ILCode.Ceq;
+
+ // when comparing signed integral values using Cle_Un with 0
+ // the Ast should actually contain Equality since "(uint)a <= 0u" is identical to "a == 0"
+ if (arg1Type.IsSignedIntegralType())
+ {
+ var p = arg2 as PrimitiveExpression;
+ if (p != null && p.Value.IsZero()) goto case ILCode.Ceq;
+ }
+
+ goto case ILCode.Cle;
+ }
+ case ILCode.Cle: return new BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2);
+ case ILCode.Cge_Un:
+ case ILCode.Cge: return new BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2);
+ case ILCode.Clt_Un:
+ case ILCode.Clt: return new BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
+ #endregion
+ #region Logical
+ case ILCode.LogicNot: return new UnaryOperatorExpression(UnaryOperatorType.Not, arg1);
+ case ILCode.LogicAnd: return new BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalAnd, arg2);
+ case ILCode.LogicOr: return new BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalOr, arg2);
+ case ILCode.TernaryOp: return new ConditionalExpression() { Condition = arg1, TrueExpression = arg2, FalseExpression = arg3 };
+ case ILCode.NullCoalescing: return new BinaryOperatorExpression(arg1, BinaryOperatorType.NullCoalescing, arg2);
+ #endregion
+ #region Branch
+ case ILCode.Br: return new GotoStatement(((ILLabel)byteCode.Operand).Name);
+ case ILCode.Brtrue:
+ return new IfElseStatement() {
+ Condition = arg1,
+ TrueStatement = new BlockStatement() {
+ new GotoStatement(((ILLabel)byteCode.Operand).Name)
+ }
+ };
+ case ILCode.LoopOrSwitchBreak: return new BreakStatement();
+ case ILCode.LoopContinue: return new ContinueStatement();
+ #endregion
+ #region Conversions
+ case ILCode.Conv_I1:
+ case ILCode.Conv_I2:
+ case ILCode.Conv_I4:
+ case ILCode.Conv_I8:
+ case ILCode.Conv_U1:
+ case ILCode.Conv_U2:
+ case ILCode.Conv_U4:
+ case ILCode.Conv_U8:
+ case ILCode.Conv_I:
+ case ILCode.Conv_U:
+ {
+ // conversion was handled by Convert() function using the info from type analysis
+ CastExpression cast = arg1 as CastExpression;
+ if (cast != null) {
+ cast.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation);
+ }
+ return arg1;
+ }
+ case ILCode.Conv_R4:
+ case ILCode.Conv_R8:
+ case ILCode.Conv_R_Un: // TODO
+ return arg1;
+ case ILCode.Conv_Ovf_I1:
+ case ILCode.Conv_Ovf_I2:
+ case ILCode.Conv_Ovf_I4:
+ case ILCode.Conv_Ovf_I8:
+ case ILCode.Conv_Ovf_U1:
+ case ILCode.Conv_Ovf_U2:
+ case ILCode.Conv_Ovf_U4:
+ case ILCode.Conv_Ovf_U8:
+ case ILCode.Conv_Ovf_I1_Un:
+ case ILCode.Conv_Ovf_I2_Un:
+ case ILCode.Conv_Ovf_I4_Un:
+ case ILCode.Conv_Ovf_I8_Un:
+ case ILCode.Conv_Ovf_U1_Un:
+ case ILCode.Conv_Ovf_U2_Un:
+ case ILCode.Conv_Ovf_U4_Un:
+ case ILCode.Conv_Ovf_U8_Un:
+ case ILCode.Conv_Ovf_I:
+ case ILCode.Conv_Ovf_U:
+ case ILCode.Conv_Ovf_I_Un:
+ case ILCode.Conv_Ovf_U_Un:
+ {
+ // conversion was handled by Convert() function using the info from type analysis
+ CastExpression cast = arg1 as CastExpression;
+ if (cast != null) {
+ cast.AddAnnotation(AddCheckedBlocks.CheckedAnnotation);
+ }
+ return arg1;
+ }
+ case ILCode.Unbox_Any:
+ // unboxing does not require a cast if the argument was an isinst instruction
+ if (arg1 is AsExpression && byteCode.Arguments[0].Code == ILCode.Isinst && TypeAnalysis.IsSameType(operand as TypeReference, byteCode.Arguments[0].Operand as TypeReference))
+ return arg1;
+ else
+ goto case ILCode.Castclass;
+ case ILCode.Castclass:
+ if ((byteCode.Arguments[0].InferredType != null && byteCode.Arguments[0].InferredType.IsGenericParameter) || ((TypeReference)operand).IsGenericParameter)
+ return arg1.CastTo(new PrimitiveType("object")).CastTo(operandAsTypeRef);
+ else
+ return arg1.CastTo(operandAsTypeRef);
+ case ILCode.Isinst:
+ return arg1.CastAs(operandAsTypeRef);
+ case ILCode.Box:
+ return arg1;
+ case ILCode.Unbox:
+ return MakeRef(arg1.CastTo(operandAsTypeRef));
+ #endregion
+ #region Indirect
+ case ILCode.Ldind_Ref:
+ case ILCode.Ldobj:
+ if (arg1 is DirectionExpression)
+ return ((DirectionExpression)arg1).Expression.Detach();
+ else
+ return new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg1);
+ case ILCode.Stind_Ref:
+ case ILCode.Stobj:
+ if (arg1 is DirectionExpression)
+ return new AssignmentExpression(((DirectionExpression)arg1).Expression.Detach(), arg2);
+ else
+ return new AssignmentExpression(new UnaryOperatorExpression(UnaryOperatorType.Dereference, arg1), arg2);
+ #endregion
+ case ILCode.Arglist:
+ return new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.ArgListAccess };
+ case ILCode.Break: return InlineAssembly(byteCode, args);
+ case ILCode.Call:
+ case ILCode.CallGetter:
+ case ILCode.CallSetter:
+ return TransformCall(false, byteCode, args);
+ case ILCode.Callvirt:
+ case ILCode.CallvirtGetter:
+ case ILCode.CallvirtSetter:
+ return TransformCall(true, byteCode, args);
+ case ILCode.Ldftn: {
+ MethodReference cecilMethod = ((MethodReference)operand);
+ var expr = new IdentifierExpression(cecilMethod.Name);
+ expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod));
+ expr.AddAnnotation(cecilMethod);
+ return new IdentifierExpression("ldftn").Invoke(expr)
+ .WithAnnotation(new DelegateConstruction.Annotation(false));
+ }
+ case ILCode.Ldvirtftn: {
+ MethodReference cecilMethod = ((MethodReference)operand);
+ var expr = new IdentifierExpression(cecilMethod.Name);
+ expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod));
+ expr.AddAnnotation(cecilMethod);
+ return new IdentifierExpression("ldvirtftn").Invoke(expr)
+ .WithAnnotation(new DelegateConstruction.Annotation(true));
+ }
+ case ILCode.Calli: return InlineAssembly(byteCode, args);
+ case ILCode.Ckfinite: return InlineAssembly(byteCode, args);
+ case ILCode.Constrained: return InlineAssembly(byteCode, args);
+ case ILCode.Cpblk: return InlineAssembly(byteCode, args);
+ case ILCode.Cpobj: return InlineAssembly(byteCode, args);
+ case ILCode.Dup: return arg1;
+ case ILCode.Endfilter: return InlineAssembly(byteCode, args);
+ case ILCode.Endfinally: return null;
+ case ILCode.Initblk: return InlineAssembly(byteCode, args);
+ case ILCode.Initobj: return InlineAssembly(byteCode, args);
+ case ILCode.DefaultValue:
+ return MakeDefaultValue((TypeReference)operand);
+ case ILCode.Jmp: return InlineAssembly(byteCode, args);
+ case ILCode.Ldc_I4:
+ return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType);
+ case ILCode.Ldc_I8:
+ return AstBuilder.MakePrimitive((long)operand, byteCode.InferredType);
+ case ILCode.Ldc_R4:
+ case ILCode.Ldc_R8:
+ case ILCode.Ldc_Decimal:
+ return new PrimitiveExpression(operand);
+ case ILCode.Ldfld:
+ if (arg1 is DirectionExpression)
+ arg1 = ((DirectionExpression)arg1).Expression.Detach();
+ return arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand);
+ case ILCode.Ldsfld:
+ return AstBuilder.ConvertType(((FieldReference)operand).DeclaringType)
+ .Member(((FieldReference)operand).Name).WithAnnotation(operand);
+ case ILCode.Stfld:
+ if (arg1 is DirectionExpression)
+ arg1 = ((DirectionExpression)arg1).Expression.Detach();
+ return new AssignmentExpression(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand), arg2);
+ case ILCode.Stsfld:
+ return new AssignmentExpression(
+ AstBuilder.ConvertType(((FieldReference)operand).DeclaringType)
+ .Member(((FieldReference)operand).Name).WithAnnotation(operand),
+ arg1);
+ case ILCode.Ldflda:
+ if (arg1 is DirectionExpression)
+ arg1 = ((DirectionExpression)arg1).Expression.Detach();
+ return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand));
+ case ILCode.Ldsflda:
+ return MakeRef(
+ AstBuilder.ConvertType(((FieldReference)operand).DeclaringType)
+ .Member(((FieldReference)operand).Name).WithAnnotation(operand));
+ case ILCode.Ldloc: {
+ ILVariable v = (ILVariable)operand;
+ if (!v.IsParameter)
+ localVariablesToDefine.Add((ILVariable)operand);
+ Expression expr;
+ if (v.IsParameter && v.OriginalParameter.Index < 0)
+ expr = new ThisReferenceExpression();
+ else
+ expr = new IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand);
+ return v.IsParameter && v.Type is ByReferenceType ? MakeRef(expr) : expr;
+ }
+ case ILCode.Ldloca: {
+ ILVariable v = (ILVariable)operand;
+ if (v.IsParameter && v.OriginalParameter.Index < 0)
+ return MakeRef(new ThisReferenceExpression());
+ if (!v.IsParameter)
+ localVariablesToDefine.Add((ILVariable)operand);
+ return MakeRef(new IdentifierExpression(((ILVariable)operand).Name).WithAnnotation(operand));
+ }
+ case ILCode.Ldnull: return new NullReferenceExpression();
+ case ILCode.Ldstr: return new PrimitiveExpression(operand);
+ case ILCode.Ldtoken:
+ if (operand is TypeReference) {
+ return AstBuilder.CreateTypeOfExpression((TypeReference)operand).Member("TypeHandle");
+ } else {
+ Expression referencedEntity;
+ string loadName;
+ string handleName;
+ if (operand is FieldReference) {
+ loadName = "fieldof";
+ handleName = "FieldHandle";
+ FieldReference fr = (FieldReference)operand;
+ referencedEntity = AstBuilder.ConvertType(fr.DeclaringType).Member(fr.Name).WithAnnotation(fr);
+ } else if (operand is MethodReference) {
+ loadName = "methodof";
+ handleName = "MethodHandle";
+ MethodReference mr = (MethodReference)operand;
+ var methodParameters = mr.Parameters.Select(p => new TypeReferenceExpression(AstBuilder.ConvertType(p.ParameterType)));
+ referencedEntity = AstBuilder.ConvertType(mr.DeclaringType).Invoke(mr.Name, methodParameters).WithAnnotation(mr);
+ } else {
+ loadName = "ldtoken";
+ handleName = "Handle";
+ referencedEntity = new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand));
+ }
+ return new IdentifierExpression(loadName).Invoke(referencedEntity).WithAnnotation(new LdTokenAnnotation()).Member(handleName);
+ }
+ case ILCode.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name };
+ case ILCode.Localloc:
+ {
+ PointerType ptrType = byteCode.InferredType as PointerType;
+ TypeReference type;
+ if (ptrType != null) {
+ type = ptrType.ElementType;
+ } else {
+ type = typeSystem.Byte;
+ }
+ return new StackAllocExpression {
+ Type = AstBuilder.ConvertType(type),
+ CountExpression = arg1
+ };
+ }
+ case ILCode.Mkrefany:
+ {
+ DirectionExpression dir = arg1 as DirectionExpression;
+ if (dir != null) {
+ return new UndocumentedExpression {
+ UndocumentedExpressionType = UndocumentedExpressionType.MakeRef,
+ Arguments = { dir.Expression.Detach() }
+ };
+ } else {
+ return InlineAssembly(byteCode, args);
+ }
+ }
+ case ILCode.Refanytype:
+ return new UndocumentedExpression {
+ UndocumentedExpressionType = UndocumentedExpressionType.RefType,
+ Arguments = { arg1 }
+ }.Member("TypeHandle");
+ case ILCode.Refanyval:
+ return MakeRef(
+ new UndocumentedExpression {
+ UndocumentedExpressionType = UndocumentedExpressionType.RefValue,
+ Arguments = { arg1, new TypeReferenceExpression(operandAsTypeRef) }
+ });
+ case ILCode.Newobj: {
+ TypeReference declaringType = ((MethodReference)operand).DeclaringType;
+ if (declaringType is ArrayType) {
+ ComposedType ct = AstBuilder.ConvertType((ArrayType)declaringType) as ComposedType;
+ if (ct != null && ct.ArraySpecifiers.Count >= 1) {
+ var ace = new ArrayCreateExpression();
+ ct.ArraySpecifiers.First().Remove();
+ ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
+ ace.Type = ct;
+ ace.Arguments.AddRange(args);
+ return ace;
+ }
+ }
+ if (declaringType.IsAnonymousType()) {
+ MethodDefinition ctor = ((MethodReference)operand).Resolve();
+ if (methodDef != null) {
+ AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression();
+ if (CanInferAnonymousTypePropertyNamesFromArguments(args, ctor.Parameters)) {
+ atce.Initializers.AddRange(args);
+ } else {
+ for (int i = 0; i < args.Count; i++) {
+ atce.Initializers.Add(
+ new NamedExpression {
+ Name = ctor.Parameters[i].Name,
+ Expression = args[i]
+ });
+ }
+ }
+ return atce;
+ }
+ }
+ var oce = new ObjectCreateExpression();
+ oce.Type = AstBuilder.ConvertType(declaringType);
+ oce.Arguments.AddRange(args);
+ return oce.WithAnnotation(operand);
+ }
+ case ILCode.No: return InlineAssembly(byteCode, args);
+ case ILCode.Nop: return null;
+ case ILCode.Pop: return arg1;
+ case ILCode.Readonly: return InlineAssembly(byteCode, args);
+ case ILCode.Ret:
+ if (methodDef.ReturnType.FullName != "System.Void") {
+ return new ReturnStatement { Expression = arg1 };
+ } else {
+ return new ReturnStatement();
+ }
+ case ILCode.Rethrow: return new ThrowStatement();
+ case ILCode.Sizeof: return new SizeOfExpression { Type = operandAsTypeRef };
+ case ILCode.Stloc: {
+ ILVariable locVar = (ILVariable)operand;
+ if (!locVar.IsParameter)
+ localVariablesToDefine.Add(locVar);
+ return new AssignmentExpression(new IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1);
+ }
+ case ILCode.Switch: return InlineAssembly(byteCode, args);
+ case ILCode.Tail: return InlineAssembly(byteCode, args);
+ case ILCode.Throw: return new ThrowStatement { Expression = arg1 };
+ case ILCode.Unaligned: return InlineAssembly(byteCode, args);
+ case ILCode.Volatile: return InlineAssembly(byteCode, args);
+ case ILCode.YieldBreak:
+ return new YieldBreakStatement();
+ case ILCode.YieldReturn:
+ return new YieldReturnStatement { Expression = arg1 };
+ case ILCode.InitObject:
+ case ILCode.InitCollection:
+ {
+ ArrayInitializerExpression initializer = new ArrayInitializerExpression();
+ for (int i = 1; i < args.Count; i++) {
+ Match m = objectInitializerPattern.Match(args[i]);
+ if (m.Success) {
+ MemberReferenceExpression mre = m.Get<MemberReferenceExpression>("left").Single();
+ initializer.Elements.Add(
+ new NamedExpression {
+ Name = mre.MemberName,
+ Expression = m.Get<Expression>("right").Single().Detach()
+ }.CopyAnnotationsFrom(mre));
+ } else {
+ m = collectionInitializerPattern.Match(args[i]);
+ if (m.Success) {
+ if (m.Get("arg").Count() == 1) {
+ initializer.Elements.Add(m.Get<Expression>("arg").Single().Detach());
+ } else {
+ ArrayInitializerExpression argList = new ArrayInitializerExpression();
+ foreach (var expr in m.Get<Expression>("arg")) {
+ argList.Elements.Add(expr.Detach());
+ }
+ initializer.Elements.Add(argList);
+ }
+ } else {
+ initializer.Elements.Add(args[i]);
+ }
+ }
+ }
+ ObjectCreateExpression oce = arg1 as ObjectCreateExpression;
+ DefaultValueExpression dve = arg1 as DefaultValueExpression;
+ if (oce != null) {
+ oce.Initializer = initializer;
+ return oce;
+ } else if (dve != null) {
+ oce = new ObjectCreateExpression(dve.Type.Detach());
+ oce.CopyAnnotationsFrom(dve);
+ oce.Initializer = initializer;
+ return oce;
+ } else {
+ return new AssignmentExpression(arg1, initializer);
+ }
+ }
+ case ILCode.InitializedObject:
+ return new InitializedObjectExpression();
+ case ILCode.Wrap:
+ return arg1.WithAnnotation(PushNegation.LiftedOperatorAnnotation);
+ case ILCode.AddressOf:
+ return MakeRef(arg1);
+ case ILCode.ExpressionTreeParameterDeclarations:
+ args[args.Count - 1].AddAnnotation(new ParameterDeclarationAnnotation(byteCode));
+ return args[args.Count - 1];
+ case ILCode.Await:
+ return new UnaryOperatorExpression(UnaryOperatorType.Await, UnpackDirectionExpression(arg1));
+ case ILCode.NullableOf:
+ case ILCode.ValueOf:
+ return arg1;
+ default:
+ throw new Exception("Unknown OpCode: " + byteCode.Code);
+ }
+ }
+
+ internal static bool CanInferAnonymousTypePropertyNamesFromArguments(IList<Expression> args, IList<ParameterDefinition> parameters)
+ {
+ for (int i = 0; i < args.Count; i++) {
+ string inferredName;
+ if (args[i] is IdentifierExpression)
+ inferredName = ((IdentifierExpression)args[i]).Identifier;
+ else if (args[i] is MemberReferenceExpression)
+ inferredName = ((MemberReferenceExpression)args[i]).MemberName;
+ else
+ inferredName = null;
+
+ if (inferredName != parameters[i].Name) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static readonly AstNode objectInitializerPattern = new AssignmentExpression(
+ new MemberReferenceExpression {
+ Target = new InitializedObjectExpression(),
+ MemberName = Pattern.AnyString
+ }.WithName("left"),
+ new AnyNode("right")
+ );
+
+ static readonly AstNode collectionInitializerPattern = new InvocationExpression {
+ Target = new MemberReferenceExpression {
+ Target = new InitializedObjectExpression(),
+ MemberName = "Add"
+ },
+ Arguments = { new Repeat(new AnyNode("arg")) }
+ };
+
+ sealed class InitializedObjectExpression : IdentifierExpression
+ {
+ public InitializedObjectExpression() : base("__initialized_object__") {}
+
+ protected override bool DoMatch(AstNode other, Match match)
+ {
+ return other is InitializedObjectExpression;
+ }
+ }
+
+ Expression MakeDefaultValue(TypeReference type)
+ {
+ TypeDefinition typeDef = type.Resolve();
+ if (typeDef != null) {
+ if (TypeAnalysis.IsIntegerOrEnum(typeDef))
+ return AstBuilder.MakePrimitive(0, typeDef);
+ else if (!typeDef.IsValueType)
+ return new NullReferenceExpression();
+ switch (typeDef.FullName) {
+ case "System.Nullable`1":
+ return new NullReferenceExpression();
+ case "System.Single":
+ return new PrimitiveExpression(0f);
+ case "System.Double":
+ return new PrimitiveExpression(0.0);
+ case "System.Decimal":
+ return new PrimitiveExpression(0m);
+ }
+ }
+ return new DefaultValueExpression { Type = AstBuilder.ConvertType(type) };
+ }
+
+ AstNode TransformCall(bool isVirtual, ILExpression byteCode, List<Expression> args)
+ {
+ MethodReference cecilMethod = (MethodReference)byteCode.Operand;
+ MethodDefinition cecilMethodDef = cecilMethod.Resolve();
+ Expression target;
+ List<Expression> methodArgs = new List<Expression>(args);
+ if (cecilMethod.HasThis) {
+ target = methodArgs[0];
+ methodArgs.RemoveAt(0);
+
+ // Unpack any DirectionExpression that is used as target for the call
+ // (calling methods on value types implicitly passes the first argument by reference)
+ target = UnpackDirectionExpression(target);
+
+ if (cecilMethodDef != null) {
+ // convert null.ToLower() to ((string)null).ToLower()
+ if (target is NullReferenceExpression)
+ target = target.CastTo(AstBuilder.ConvertType(cecilMethod.DeclaringType));
+
+ if (cecilMethodDef.DeclaringType.IsInterface) {
+ TypeReference tr = byteCode.Arguments[0].InferredType;
+ if (tr != null) {
+ TypeDefinition td = tr.Resolve();
+ if (td != null && !td.IsInterface) {
+ // Calling an interface method on a non-interface object:
+ // we need to introduce an explicit cast
+ target = target.CastTo(AstBuilder.ConvertType(cecilMethod.DeclaringType));
+ }
+ }
+ }
+ }
+ } else {
+ target = new TypeReferenceExpression { Type = AstBuilder.ConvertType(cecilMethod.DeclaringType) };
+ }
+ if (target is ThisReferenceExpression && !isVirtual) {
+ // a non-virtual call on "this" might be a "base"-call.
+ if (cecilMethod.DeclaringType.GetElementType() != methodDef.DeclaringType) {
+ // If we're not calling a method in the current class; we must be calling one in the base class.
+ target = new BaseReferenceExpression();
+ }
+ }
+
+ if (cecilMethod.Name == ".ctor" && cecilMethod.DeclaringType.IsValueType) {
+ // On value types, the constructor can be called.
+ // This is equivalent to 'target = new ValueType(args);'.
+ ObjectCreateExpression oce = new ObjectCreateExpression();
+ oce.Type = AstBuilder.ConvertType(cecilMethod.DeclaringType);
+ oce.AddAnnotation(cecilMethod);
+ AdjustArgumentsForMethodCall(cecilMethod, methodArgs);
+ oce.Arguments.AddRange(methodArgs);
+ return new AssignmentExpression(target, oce);
+ }
+
+ if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) {
+ return target.Indexer(methodArgs);
+ } else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) {
+ return new AssignmentExpression(target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)), methodArgs.Last());
+ }
+
+ // Test whether the method is an accessor:
+ if (cecilMethodDef != null) {
+ if (cecilMethodDef.IsGetter && methodArgs.Count == 0) {
+ foreach (var prop in cecilMethodDef.DeclaringType.Properties) {
+ if (prop.GetMethod == cecilMethodDef)
+ return target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod);
+ }
+ } else if (cecilMethodDef.IsGetter) { // with parameters
+ PropertyDefinition indexer = GetIndexer(cecilMethodDef);
+ if (indexer != null)
+ return target.Indexer(methodArgs).WithAnnotation(indexer).WithAnnotation(cecilMethod);
+ } else if (cecilMethodDef.IsSetter && methodArgs.Count == 1) {
+ foreach (var prop in cecilMethodDef.DeclaringType.Properties) {
+ if (prop.SetMethod == cecilMethodDef)
+ return new AssignmentExpression(target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod), methodArgs[0]);
+ }
+ } else if (cecilMethodDef.IsSetter && methodArgs.Count > 1) {
+ PropertyDefinition indexer = GetIndexer(cecilMethodDef);
+ if (indexer != null)
+ return new AssignmentExpression(
+ target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)).WithAnnotation(indexer).WithAnnotation(cecilMethod),
+ methodArgs[methodArgs.Count - 1]
+ );
+ } else if (cecilMethodDef.IsAddOn && methodArgs.Count == 1) {
+ foreach (var ev in cecilMethodDef.DeclaringType.Events) {
+ if (ev.AddMethod == cecilMethodDef) {
+ return new AssignmentExpression {
+ Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod),
+ Operator = AssignmentOperatorType.Add,
+ Right = methodArgs[0]
+ };
+ }
+ }
+ } else if (cecilMethodDef.IsRemoveOn && methodArgs.Count == 1) {
+ foreach (var ev in cecilMethodDef.DeclaringType.Events) {
+ if (ev.RemoveMethod == cecilMethodDef) {
+ return new AssignmentExpression {
+ Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod),
+ Operator = AssignmentOperatorType.Subtract,
+ Right = methodArgs[0]
+ };
+ }
+ }
+ } else if (cecilMethodDef.Name == "Invoke" && cecilMethodDef.DeclaringType.BaseType != null && cecilMethodDef.DeclaringType.BaseType.FullName == "System.MulticastDelegate") {
+ AdjustArgumentsForMethodCall(cecilMethod, methodArgs);
+ return target.Invoke(methodArgs).WithAnnotation(cecilMethod);
+ }
+ }
+ // Default invocation
+ AdjustArgumentsForMethodCall(cecilMethodDef ?? cecilMethod, methodArgs);
+ return target.Invoke(cecilMethod.Name, ConvertTypeArguments(cecilMethod), methodArgs).WithAnnotation(cecilMethod);
+ }
+
+ static Expression UnpackDirectionExpression(Expression target)
+ {
+ if (target is DirectionExpression) {
+ return ((DirectionExpression)target).Expression.Detach();
+ } else {
+ return target;
+ }
+ }
+
+ static void AdjustArgumentsForMethodCall(MethodReference cecilMethod, List<Expression> methodArgs)
+ {
+ // Convert 'ref' into 'out' where necessary
+ for (int i = 0; i < methodArgs.Count && i < cecilMethod.Parameters.Count; i++) {
+ DirectionExpression dir = methodArgs[i] as DirectionExpression;
+ ParameterDefinition p = cecilMethod.Parameters[i];
+ if (dir != null && p.IsOut && !p.IsIn)
+ dir.FieldDirection = FieldDirection.Out;
+ }
+ }
+
+ internal static PropertyDefinition GetIndexer(MethodDefinition cecilMethodDef)
+ {
+ TypeDefinition typeDef = cecilMethodDef.DeclaringType;
+ string indexerName = null;
+ foreach (CustomAttribute ca in typeDef.CustomAttributes) {
+ if (ca.Constructor.FullName == "System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") {
+ indexerName = ca.ConstructorArguments.Single().Value as string;
+ break;
+ }
+ }
+ if (indexerName == null)
+ return null;
+ foreach (PropertyDefinition prop in typeDef.Properties) {
+ if (prop.Name == indexerName) {
+ if (prop.GetMethod == cecilMethodDef || prop.SetMethod == cecilMethodDef)
+ return prop;
+ }
+ }
+ return null;
+ }
+
+ #if DEBUG
+ static readonly ConcurrentDictionary<ILCode, int> unhandledOpcodes = new ConcurrentDictionary<ILCode, int>();
+ #endif
+
+ [Conditional("DEBUG")]
+ public static void ClearUnhandledOpcodes()
+ {
+ #if DEBUG
+ unhandledOpcodes.Clear();
+ #endif
+ }
+
+ [Conditional("DEBUG")]
+ public static void PrintNumberOfUnhandledOpcodes()
+ {
+ #if DEBUG
+ foreach (var pair in unhandledOpcodes) {
+ Debug.WriteLine("AddMethodBodyBuilder unhandled opcode: {1}x {0}", pair.Key, pair.Value);
+ }
+ #endif
+ }
+
+ static Expression InlineAssembly(ILExpression byteCode, List<Expression> args)
+ {
+ #if DEBUG
+ unhandledOpcodes.AddOrUpdate(byteCode.Code, c => 1, (c, n) => n+1);
+ #endif
+ // Output the operand of the unknown IL code as well
+ if (byteCode.Operand != null) {
+ args.Insert(0, new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand)));
+ }
+ return new IdentifierExpression(byteCode.Code.GetName()).Invoke(args);
+ }
+
+ static string FormatByteCodeOperand(object operand)
+ {
+ if (operand == null) {
+ return string.Empty;
+ //} else if (operand is ILExpression) {
+ // return string.Format("IL_{0:X2}", ((ILExpression)operand).Offset);
+ } else if (operand is MethodReference) {
+ return ((MethodReference)operand).Name + "()";
+ } else if (operand is TypeReference) {
+ return ((TypeReference)operand).FullName;
+ } else if (operand is VariableDefinition) {
+ return ((VariableDefinition)operand).Name;
+ } else if (operand is ParameterDefinition) {
+ return ((ParameterDefinition)operand).Name;
+ } else if (operand is FieldReference) {
+ return ((FieldReference)operand).Name;
+ } else if (operand is string) {
+ return "\"" + operand + "\"";
+ } else if (operand is int) {
+ return operand.ToString();
+ } else {
+ return operand.ToString();
+ }
+ }
+
+ static IEnumerable<AstType> ConvertTypeArguments(MethodReference cecilMethod)
+ {
+ GenericInstanceMethod g = cecilMethod as GenericInstanceMethod;
+ if (g == null)
+ return null;
+ if (g.GenericArguments.Any(ta => ta.ContainsAnonymousType()))
+ return null;
+ return g.GenericArguments.Select(t => AstBuilder.ConvertType(t));
+ }
+
+ static DirectionExpression MakeRef(Expression expr)
+ {
+ return new DirectionExpression { Expression = expr, FieldDirection = FieldDirection.Ref };
+ }
+
+ Expression Convert(Expression expr, TypeReference actualType, TypeReference reqType)
+ {
+ if (actualType == null || reqType == null || TypeAnalysis.IsSameType(actualType, reqType)) {
+ return expr;
+ } else if (actualType is ByReferenceType && reqType is PointerType && expr is DirectionExpression) {
+ return Convert(
+ new UnaryOperatorExpression(UnaryOperatorType.AddressOf, ((DirectionExpression)expr).Expression.Detach()),
+ new PointerType(((ByReferenceType)actualType).ElementType),
+ reqType);
+ } else if (actualType is PointerType && reqType is ByReferenceType) {
+ expr = Convert(expr, actualType, new PointerType(((ByReferenceType)reqType).ElementType));
+ return new DirectionExpression {
+ FieldDirection = FieldDirection.Ref,
+ Expression = new UnaryOperatorExpression(UnaryOperatorType.Dereference, expr)
+ };
+ } else if (actualType is PointerType && reqType is PointerType) {
+ if (actualType.FullName != reqType.FullName)
+ return expr.CastTo(AstBuilder.ConvertType(reqType));
+ else
+ return expr;
+ } else {
+ bool actualIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(actualType);
+ bool requiredIsIntegerOrEnum = TypeAnalysis.IsIntegerOrEnum(reqType);
+
+ if (TypeAnalysis.IsBoolean(reqType)) {
+ if (TypeAnalysis.IsBoolean(actualType))
+ return expr;
+ if (actualIsIntegerOrEnum) {
+ return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, AstBuilder.MakePrimitive(0, actualType));
+ } else {
+ return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, new NullReferenceExpression());
+ }
+ }
+ if (TypeAnalysis.IsBoolean(actualType) && requiredIsIntegerOrEnum) {
+ return new ConditionalExpression {
+ Condition = expr,
+ TrueExpression = AstBuilder.MakePrimitive(1, reqType),
+ FalseExpression = AstBuilder.MakePrimitive(0, reqType)
+ };
+ }
+
+ if (expr is PrimitiveExpression && !requiredIsIntegerOrEnum && TypeAnalysis.IsEnum(actualType))
+ {
+ return expr.CastTo(AstBuilder.ConvertType(actualType));
+ }
+
+ bool actualIsPrimitiveType = actualIsIntegerOrEnum
+ || actualType.MetadataType == MetadataType.Single || actualType.MetadataType == MetadataType.Double;
+ bool requiredIsPrimitiveType = requiredIsIntegerOrEnum
+ || reqType.MetadataType == MetadataType.Single || reqType.MetadataType == MetadataType.Double;
+ if (actualIsPrimitiveType && requiredIsPrimitiveType) {
+ return expr.CastTo(AstBuilder.ConvertType(reqType));
+ }
+ return expr;
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/CommentStatement.cs b/ICSharpCode.Decompiler/Ast/CommentStatement.cs
new file mode 100644
index 00000000..39bc2450
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/CommentStatement.cs
@@ -0,0 +1,69 @@
+// 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.Linq;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.PatternMatching;
+
+namespace ICSharpCode.Decompiler.Ast
+{
+ /// <summary>
+ /// Allows storing comments inside IEnumerable{Statement}. Used in the AstMethodBuilder.
+ /// CommentStatement nodes are replaced with regular comments later on.
+ /// </summary>
+ internal class CommentStatement : Statement
+ {
+ string comment;
+
+ public CommentStatement(string comment)
+ {
+ if (comment == null)
+ throw new ArgumentNullException("comment");
+ this.comment = comment;
+ }
+
+ public override void AcceptVisitor(IAstVisitor visitor)
+ {
+ }
+
+ public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
+ {
+ return default(T);
+ }
+
+ public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
+ {
+ return default(S);
+ }
+
+ public static void ReplaceAll(AstNode tree)
+ {
+ foreach (var cs in tree.Descendants.OfType<CommentStatement>()) {
+ cs.Parent.InsertChildBefore(cs, new Comment(cs.comment), Roles.Comment);
+ cs.Remove();
+ }
+ }
+
+ protected override bool DoMatch(AstNode other, Match match)
+ {
+ CommentStatement o = other as CommentStatement;
+ return o != null && MatchString(comment, o.comment);
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/DecompilerContext.cs b/ICSharpCode.Decompiler/Ast/DecompilerContext.cs
new file mode 100644
index 00000000..d055de1d
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/DecompilerContext.cs
@@ -0,0 +1,71 @@
+// 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.Generic;
+using System.Threading;
+using ICSharpCode.Decompiler.Ast;
+using ICSharpCode.NRefactory.TypeSystem;
+using ICSharpCode.NRefactory.TypeSystem.Implementation;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler
+{
+ public class DecompilerContext
+ {
+ public ModuleDefinition CurrentModule;
+ public CancellationToken CancellationToken;
+ public TypeDefinition CurrentType;
+ public MethodDefinition CurrentMethod;
+ public DecompilerSettings Settings = new DecompilerSettings();
+ public bool CurrentMethodIsAsync;
+
+// public ITypeResolveContext TypeResolveContext;
+// public IProjectContent ProjectContent;
+
+ public DecompilerContext(ModuleDefinition currentModule)
+ {
+ if (currentModule == null)
+ throw new ArgumentNullException("currentModule");
+ CurrentModule = currentModule;
+
+// this.ProjectContent = new CecilTypeResolveContext(currentModule);
+// List<ITypeResolveContext> resolveContexts = new List<ITypeResolveContext>();
+// resolveContexts.Add(this.ProjectContent);
+// foreach (AssemblyNameReference r in currentModule.AssemblyReferences) {
+// AssemblyDefinition d = currentModule.AssemblyResolver.Resolve(r);
+// if (d != null) {
+// resolveContexts.Add(new CecilTypeResolveContext(d.MainModule));
+// }
+// }
+// this.TypeResolveContext = new CompositeTypeResolveContext(resolveContexts);
+ }
+
+ /// <summary>
+ /// Used to pass variable names from a method to its anonymous methods.
+ /// </summary>
+ internal List<string> ReservedVariableNames = new List<string>();
+
+ public DecompilerContext Clone()
+ {
+ DecompilerContext ctx = (DecompilerContext)MemberwiseClone();
+ ctx.ReservedVariableNames = new List<string>(ctx.ReservedVariableNames);
+ return ctx;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs b/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs
new file mode 100644
index 00000000..80730061
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs
@@ -0,0 +1,86 @@
+// 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 ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.PatternMatching;
+
+namespace ICSharpCode.Decompiler.Ast
+{
+ internal static class NRefactoryExtensions
+ {
+ public static T WithAnnotation<T>(this T node, object annotation) where T : AstNode
+ {
+ if (annotation != null)
+ node.AddAnnotation(annotation);
+ return node;
+ }
+
+ public static T CopyAnnotationsFrom<T>(this T node, AstNode other) where T : AstNode
+ {
+ foreach (object annotation in other.Annotations) {
+ node.AddAnnotation(annotation);
+ }
+ return node;
+ }
+
+ public static T Detach<T>(this T node) where T : AstNode
+ {
+ node.Remove();
+ return node;
+ }
+
+ public static Expression WithName(this Expression node, string patternGroupName)
+ {
+ return new NamedNode(patternGroupName, node);
+ }
+
+ public static Statement WithName(this Statement node, string patternGroupName)
+ {
+ return new NamedNode(patternGroupName, node);
+ }
+
+ public static void AddNamedArgument(this NRefactory.CSharp.Attribute attribute, string name, Expression argument)
+ {
+ attribute.Arguments.Add(new AssignmentExpression(new IdentifierExpression(name), argument));
+ }
+
+ public static AstType ToType(this Pattern pattern)
+ {
+ return pattern;
+ }
+
+ public static Expression ToExpression(this Pattern pattern)
+ {
+ return pattern;
+ }
+
+ public static Statement ToStatement(this Pattern pattern)
+ {
+ return pattern;
+ }
+
+ public static Statement GetNextStatement(this Statement statement)
+ {
+ AstNode next = statement.NextSibling;
+ while (next != null && !(next is Statement))
+ next = next.NextSibling;
+ return (Statement)next;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/NameVariables.cs b/ICSharpCode.Decompiler/Ast/NameVariables.cs
new file mode 100644
index 00000000..3da808cb
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/NameVariables.cs
@@ -0,0 +1,347 @@
+// 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.Generic;
+using System.Linq;
+
+using ICSharpCode.Decompiler.ILAst;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast
+{
+ public class NameVariables
+ {
+ static readonly Dictionary<string, string> typeNameToVariableNameDict = new Dictionary<string, string> {
+ { "System.Boolean", "flag" },
+ { "System.Byte", "b" },
+ { "System.SByte", "b" },
+ { "System.Int16", "num" },
+ { "System.Int32", "num" },
+ { "System.Int64", "num" },
+ { "System.UInt16", "num" },
+ { "System.UInt32", "num" },
+ { "System.UInt64", "num" },
+ { "System.Single", "num" },
+ { "System.Double", "num" },
+ { "System.Decimal", "num" },
+ { "System.String", "text" },
+ { "System.Object", "obj" },
+ { "System.Char", "c" }
+ };
+
+
+ public static void AssignNamesToVariables(DecompilerContext context, IEnumerable<ILVariable> parameters, IEnumerable<ILVariable> variables, ILBlock methodBody)
+ {
+ NameVariables nv = new NameVariables();
+ nv.context = context;
+ nv.fieldNamesInCurrentType = context.CurrentType.Fields.Select(f => f.Name).ToList();
+ // First mark existing variable names as reserved.
+ foreach (string name in context.ReservedVariableNames)
+ nv.AddExistingName(name);
+ foreach (var p in parameters)
+ nv.AddExistingName(p.Name);
+ foreach (var v in variables) {
+ if (v.IsGenerated) {
+ // don't introduce names for variables generated by ILSpy - keep "expr"/"arg"
+ nv.AddExistingName(v.Name);
+ } else if (v.OriginalVariable != null && context.Settings.UseDebugSymbols) {
+ string varName = v.OriginalVariable.Name;
+ if (string.IsNullOrEmpty(varName) || varName.StartsWith("V_", StringComparison.Ordinal) || !IsValidName(varName))
+ {
+ // don't use the name from the debug symbols if it looks like a generated name
+ v.Name = null;
+ } else {
+ // use the name from the debug symbols
+ // (but ensure we don't use the same name for two variables)
+ v.Name = nv.GetAlternativeName(varName);
+ }
+ } else {
+ v.Name = null;
+ }
+ }
+ // Now generate names:
+ foreach (ILVariable p in parameters) {
+ if (string.IsNullOrEmpty(p.Name))
+ p.Name = nv.GenerateNameForVariable(p, methodBody);
+ }
+ foreach (ILVariable varDef in variables) {
+ if (string.IsNullOrEmpty(varDef.Name))
+ varDef.Name = nv.GenerateNameForVariable(varDef, methodBody);
+ }
+ }
+
+ static bool IsValidName(string varName)
+ {
+ if (string.IsNullOrEmpty(varName))
+ return false;
+ if (!(char.IsLetter(varName[0]) || varName[0] == '_'))
+ return false;
+ for (int i = 1; i < varName.Length; i++) {
+ if (!(char.IsLetterOrDigit(varName[i]) || varName[i] == '_'))
+ return false;
+ }
+ return true;
+ }
+
+ DecompilerContext context;
+ List<string> fieldNamesInCurrentType;
+ Dictionary<string, int> typeNames = new Dictionary<string, int>();
+
+ public void AddExistingName(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ return;
+ int number;
+ string nameWithoutDigits = SplitName(name, out number);
+ int existingNumber;
+ if (typeNames.TryGetValue(nameWithoutDigits, out existingNumber)) {
+ typeNames[nameWithoutDigits] = Math.Max(number, existingNumber);
+ } else {
+ typeNames.Add(nameWithoutDigits, number);
+ }
+ }
+
+ string SplitName(string name, out int number)
+ {
+ // First, identify whether the name already ends with a number:
+ int pos = name.Length;
+ while (pos > 0 && name[pos-1] >= '0' && name[pos-1] <= '9')
+ pos--;
+ if (pos < name.Length) {
+ if (int.TryParse(name.Substring(pos), out number)) {
+ return name.Substring(0, pos);
+ }
+ }
+ number = 1;
+ return name;
+ }
+
+ const char maxLoopVariableName = 'n';
+
+ public string GetAlternativeName(string oldVariableName)
+ {
+ if (oldVariableName.Length == 1 && oldVariableName[0] >= 'i' && oldVariableName[0] <= maxLoopVariableName) {
+ for (char c = 'i'; c <= maxLoopVariableName; c++) {
+ if (!typeNames.ContainsKey(c.ToString())) {
+ typeNames.Add(c.ToString(), 1);
+ return c.ToString();
+ }
+ }
+ }
+
+ int number;
+ string nameWithoutDigits = SplitName(oldVariableName, out number);
+
+ if (!typeNames.ContainsKey(nameWithoutDigits)) {
+ typeNames.Add(nameWithoutDigits, number - 1);
+ }
+ int count = ++typeNames[nameWithoutDigits];
+ if (count != 1) {
+ return nameWithoutDigits + count.ToString();
+ } else {
+ return nameWithoutDigits;
+ }
+ }
+
+ string GenerateNameForVariable(ILVariable variable, ILBlock methodBody)
+ {
+ string proposedName = null;
+ if (variable.Type == context.CurrentType.Module.TypeSystem.Int32) {
+ // test whether the variable might be a loop counter
+ bool isLoopCounter = false;
+ foreach (ILWhileLoop loop in methodBody.GetSelfAndChildrenRecursive<ILWhileLoop>()) {
+ ILExpression expr = loop.Condition;
+ while (expr != null && expr.Code == ILCode.LogicNot)
+ expr = expr.Arguments[0];
+ if (expr != null) {
+ switch (expr.Code) {
+ case ILCode.Clt:
+ case ILCode.Clt_Un:
+ case ILCode.Cgt:
+ case ILCode.Cgt_Un:
+ case ILCode.Cle:
+ case ILCode.Cle_Un:
+ case ILCode.Cge:
+ case ILCode.Cge_Un:
+ ILVariable loadVar;
+ if (expr.Arguments[0].Match(ILCode.Ldloc, out loadVar) && loadVar == variable) {
+ isLoopCounter = true;
+ }
+ break;
+ }
+ }
+ }
+ if (isLoopCounter) {
+ // For loop variables, use i,j,k,l,m,n
+ for (char c = 'i'; c <= maxLoopVariableName; c++) {
+ if (!typeNames.ContainsKey(c.ToString())) {
+ proposedName = c.ToString();
+ break;
+ }
+ }
+ }
+ }
+ if (string.IsNullOrEmpty(proposedName)) {
+ var proposedNameForStores =
+ (from expr in methodBody.GetSelfAndChildrenRecursive<ILExpression>()
+ where expr.Code == ILCode.Stloc && expr.Operand == variable
+ select GetNameFromExpression(expr.Arguments.Single())
+ ).Except(fieldNamesInCurrentType).ToList();
+ if (proposedNameForStores.Count == 1) {
+ proposedName = proposedNameForStores[0];
+ }
+ }
+ if (string.IsNullOrEmpty(proposedName)) {
+ var proposedNameForLoads =
+ (from expr in methodBody.GetSelfAndChildrenRecursive<ILExpression>()
+ from i in Enumerable.Range(0, expr.Arguments.Count)
+ let arg = expr.Arguments[i]
+ where arg.Code == ILCode.Ldloc && arg.Operand == variable
+ select GetNameForArgument(expr, i)
+ ).Except(fieldNamesInCurrentType).ToList();
+ if (proposedNameForLoads.Count == 1) {
+ proposedName = proposedNameForLoads[0];
+ }
+ }
+ if (string.IsNullOrEmpty(proposedName)) {
+ proposedName = GetNameByType(variable.Type);
+ }
+
+ // remove any numbers from the proposed name
+ int number;
+ proposedName = SplitName(proposedName, out number);
+
+ if (!typeNames.ContainsKey(proposedName)) {
+ typeNames.Add(proposedName, 0);
+ }
+ int count = ++typeNames[proposedName];
+ if (count > 1) {
+ return proposedName + count.ToString();
+ } else {
+ return proposedName;
+ }
+ }
+
+ static string GetNameFromExpression(ILExpression expr)
+ {
+ switch (expr.Code) {
+ case ILCode.Ldfld:
+ case ILCode.Ldsfld:
+ return CleanUpVariableName(((FieldReference)expr.Operand).Name);
+ case ILCode.Call:
+ case ILCode.Callvirt:
+ case ILCode.CallGetter:
+ case ILCode.CallvirtGetter:
+ MethodReference mr = (MethodReference)expr.Operand;
+ if (mr.Name.StartsWith("get_", StringComparison.OrdinalIgnoreCase) && mr.Parameters.Count == 0) {
+ // use name from properties, but not from indexers
+ return CleanUpVariableName(mr.Name.Substring(4));
+ } else if (mr.Name.StartsWith("Get", StringComparison.OrdinalIgnoreCase) && mr.Name.Length >= 4 && char.IsUpper(mr.Name[3])) {
+ // use name from Get-methods
+ return CleanUpVariableName(mr.Name.Substring(3));
+ }
+ break;
+ }
+ return null;
+ }
+
+ static string GetNameForArgument(ILExpression parent, int i)
+ {
+ switch (parent.Code) {
+ case ILCode.Stfld:
+ case ILCode.Stsfld:
+ if (i == parent.Arguments.Count - 1) // last argument is stored value
+ return CleanUpVariableName(((FieldReference)parent.Operand).Name);
+ else
+ break;
+ case ILCode.Call:
+ case ILCode.Callvirt:
+ case ILCode.Newobj:
+ case ILCode.CallGetter:
+ case ILCode.CallvirtGetter:
+ case ILCode.CallSetter:
+ case ILCode.CallvirtSetter:
+ MethodReference methodRef = (MethodReference)parent.Operand;
+ if (methodRef.Parameters.Count == 1 && i == parent.Arguments.Count - 1) {
+ // argument might be value of a setter
+ if (methodRef.Name.StartsWith("set_", StringComparison.OrdinalIgnoreCase)) {
+ return CleanUpVariableName(methodRef.Name.Substring(4));
+ } else if (methodRef.Name.StartsWith("Set", StringComparison.OrdinalIgnoreCase) && methodRef.Name.Length >= 4 && char.IsUpper(methodRef.Name[3])) {
+ return CleanUpVariableName(methodRef.Name.Substring(3));
+ }
+ }
+ MethodDefinition methodDef = methodRef.Resolve();
+ if (methodDef != null) {
+ var p = methodDef.Parameters.ElementAtOrDefault((parent.Code != ILCode.Newobj && methodDef.HasThis) ? i - 1 : i);
+ if (p != null && !string.IsNullOrEmpty(p.Name))
+ return CleanUpVariableName(p.Name);
+ }
+ break;
+ case ILCode.Ret:
+ return "result";
+ }
+ return null;
+ }
+
+ string GetNameByType(TypeReference type)
+ {
+ type = TypeAnalysis.UnpackModifiers(type);
+
+ GenericInstanceType git = type as GenericInstanceType;
+ if (git != null && git.ElementType.FullName == "System.Nullable`1" && git.GenericArguments.Count == 1) {
+ type = ((GenericInstanceType)type).GenericArguments[0];
+ }
+
+ string name;
+ if (type.IsArray) {
+ name = "array";
+ } else if (type.IsPointer || type.IsByReference) {
+ name = "ptr";
+ } else if (type.Name.EndsWith("Exception", StringComparison.Ordinal)) {
+ name = "ex";
+ } else if (!typeNameToVariableNameDict.TryGetValue(type.FullName, out name)) {
+ name = type.Name;
+ // remove the 'I' for interfaces
+ if (name.Length >= 3 && name[0] == 'I' && char.IsUpper(name[1]) && char.IsLower(name[2]))
+ name = name.Substring(1);
+ name = CleanUpVariableName(name);
+ }
+ return name;
+ }
+
+ static string CleanUpVariableName(string name)
+ {
+ // remove the backtick (generics)
+ int pos = name.IndexOf('`');
+ if (pos >= 0)
+ name = name.Substring(0, pos);
+
+ // remove field prefix:
+ if (name.Length > 2 && name.StartsWith("m_", StringComparison.Ordinal))
+ name = name.Substring(2);
+ else if (name.Length > 1 && name[0] == '_')
+ name = name.Substring(1);
+
+ if (name.Length == 0)
+ return "obj";
+ else
+ return char.ToLower(name[0]) + name.Substring(1);
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs b/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs
new file mode 100644
index 00000000..fed08aaa
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs
@@ -0,0 +1,369 @@
+// 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.Generic;
+using System.Linq;
+using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.ILAst;
+using ICSharpCode.NRefactory;
+using ICSharpCode.NRefactory.CSharp;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast
+{
+ public class TextTokenWriter : TokenWriter
+ {
+ readonly ITextOutput output;
+ readonly DecompilerContext context;
+ readonly Stack<AstNode> nodeStack = new Stack<AstNode>();
+ int braceLevelWithinType = -1;
+ bool inDocumentationComment = false;
+ bool firstUsingDeclaration;
+ bool lastUsingDeclaration;
+
+ TextLocation? lastEndOfLine;
+
+ public bool FoldBraces = false;
+
+ public TextTokenWriter(ITextOutput output, DecompilerContext context)
+ {
+ if (output == null)
+ throw new ArgumentNullException("output");
+ if (context == null)
+ throw new ArgumentNullException("context");
+ this.output = output;
+ this.context = context;
+ }
+
+ public override void WriteIdentifier(Identifier identifier)
+ {
+ var definition = GetCurrentDefinition();
+ if (definition != null) {
+ output.WriteDefinition(identifier.Name, definition, false);
+ return;
+ }
+
+ object memberRef = GetCurrentMemberReference();
+
+ if (memberRef != null) {
+ output.WriteReference(identifier.Name, memberRef);
+ return;
+ }
+
+ definition = GetCurrentLocalDefinition();
+ if (definition != null) {
+ output.WriteDefinition(identifier.Name, definition);
+ return;
+ }
+
+ memberRef = GetCurrentLocalReference();
+ if (memberRef != null) {
+ output.WriteReference(identifier.Name, memberRef, true);
+ return;
+ }
+
+ if (firstUsingDeclaration) {
+ output.MarkFoldStart(defaultCollapsed: true);
+ firstUsingDeclaration = false;
+ }
+
+ output.Write(identifier.Name);
+ }
+
+ MemberReference GetCurrentMemberReference()
+ {
+ AstNode node = nodeStack.Peek();
+ MemberReference memberRef = node.Annotation<MemberReference>();
+ if (memberRef == null && node.Role == Roles.TargetExpression && (node.Parent is InvocationExpression || node.Parent is ObjectCreateExpression)) {
+ memberRef = node.Parent.Annotation<MemberReference>();
+ }
+ if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && memberRef != null) {
+ var declaringType = memberRef.DeclaringType.Resolve();
+ if (declaringType != null && declaringType.IsDelegate())
+ return null;
+ }
+ return FilterMemberReference(memberRef);
+ }
+
+ MemberReference FilterMemberReference(MemberReference memberRef)
+ {
+ if (memberRef == null)
+ return null;
+
+ if (context.Settings.AutomaticEvents && memberRef is FieldDefinition) {
+ var field = (FieldDefinition)memberRef;
+ return field.DeclaringType.Events.FirstOrDefault(ev => ev.Name == field.Name) ?? memberRef;
+ }
+
+ return memberRef;
+ }
+
+ object GetCurrentLocalReference()
+ {
+ AstNode node = nodeStack.Peek();
+ ILVariable variable = node.Annotation<ILVariable>();
+ if (variable != null) {
+ if (variable.OriginalParameter != null)
+ return variable.OriginalParameter;
+ //if (variable.OriginalVariable != null)
+ // return variable.OriginalVariable;
+ return variable;
+ }
+
+ var gotoStatement = node as GotoStatement;
+ if (gotoStatement != null)
+ {
+ var method = nodeStack.Select(nd => nd.Annotation<MethodReference>()).FirstOrDefault(mr => mr != null);
+ if (method != null)
+ return method.ToString() + gotoStatement.Label;
+ }
+
+ return null;
+ }
+
+ object GetCurrentLocalDefinition()
+ {
+ AstNode node = nodeStack.Peek();
+ if (node is Identifier && node.Parent != null)
+ node = node.Parent;
+
+ var parameterDef = node.Annotation<ParameterDefinition>();
+ if (parameterDef != null)
+ return parameterDef;
+
+ if (node is VariableInitializer || node is CatchClause || node is ForeachStatement) {
+ var variable = node.Annotation<ILVariable>();
+ if (variable != null) {
+ if (variable.OriginalParameter != null)
+ return variable.OriginalParameter;
+ //if (variable.OriginalVariable != null)
+ // return variable.OriginalVariable;
+ return variable;
+ }
+ }
+
+ var label = node as LabelStatement;
+ if (label != null) {
+ var method = nodeStack.Select(nd => nd.Annotation<MethodReference>()).FirstOrDefault(mr => mr != null);
+ if (method != null)
+ return method.ToString() + label.Label;
+ }
+
+ return null;
+ }
+
+ object GetCurrentDefinition()
+ {
+ if (nodeStack == null || nodeStack.Count == 0)
+ return null;
+
+ var node = nodeStack.Peek();
+ if (node is Identifier)
+ node = node.Parent;
+ if (IsDefinition(node))
+ return node.Annotation<MemberReference>();
+
+ return null;
+ }
+
+ public override void WriteKeyword(Role role, string keyword)
+ {
+ output.Write(keyword);
+ }
+
+ public override void WriteToken(Role role, string token)
+ {
+ // Attach member reference to token only if there's no identifier in the current node.
+ MemberReference memberRef = GetCurrentMemberReference();
+ var node = nodeStack.Peek();
+ if (memberRef != null && node.GetChildByRole(Roles.Identifier).IsNull)
+ output.WriteReference(token, memberRef);
+ else
+ output.Write(token);
+ }
+
+ public override void Space()
+ {
+ output.Write(' ');
+ }
+
+ public void OpenBrace(BraceStyle style)
+ {
+ if (braceLevelWithinType >= 0 || nodeStack.Peek() is TypeDeclaration)
+ braceLevelWithinType++;
+ if (nodeStack.OfType<BlockStatement>().Count() <= 1 || FoldBraces) {
+ output.MarkFoldStart(defaultCollapsed: braceLevelWithinType == 1);
+ }
+ output.WriteLine();
+ output.WriteLine("{");
+ output.Indent();
+ }
+
+ public void CloseBrace(BraceStyle style)
+ {
+ output.Unindent();
+ output.Write('}');
+ if (nodeStack.OfType<BlockStatement>().Count() <= 1 || FoldBraces)
+ output.MarkFoldEnd();
+ if (braceLevelWithinType >= 0)
+ braceLevelWithinType--;
+ }
+
+ public override void Indent()
+ {
+ output.Indent();
+ }
+
+ public override void Unindent()
+ {
+ output.Unindent();
+ }
+
+ public override void NewLine()
+ {
+ if (lastUsingDeclaration) {
+ output.MarkFoldEnd();
+ lastUsingDeclaration = false;
+ }
+ lastEndOfLine = output.Location;
+ output.WriteLine();
+ }
+
+ public override void WriteComment(CommentType commentType, string content)
+ {
+ switch (commentType) {
+ case CommentType.SingleLine:
+ output.Write("//");
+ output.WriteLine(content);
+ break;
+ case CommentType.MultiLine:
+ output.Write("/*");
+ output.Write(content);
+ output.Write("*/");
+ break;
+ case CommentType.Documentation:
+ bool isLastLine = !(nodeStack.Peek().NextSibling is Comment);
+ if (!inDocumentationComment && !isLastLine) {
+ inDocumentationComment = true;
+ output.MarkFoldStart("///" + content, true);
+ }
+ output.Write("///");
+ output.Write(content);
+ if (inDocumentationComment && isLastLine) {
+ inDocumentationComment = false;
+ output.MarkFoldEnd();
+ }
+ output.WriteLine();
+ break;
+ default:
+ output.Write(content);
+ break;
+ }
+ }
+
+ public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument)
+ {
+ // pre-processor directive must start on its own line
+ output.Write('#');
+ output.Write(type.ToString().ToLowerInvariant());
+ if (!string.IsNullOrEmpty(argument)) {
+ output.Write(' ');
+ output.Write(argument);
+ }
+ output.WriteLine();
+ }
+
+ public override void WritePrimitiveValue(object value, string literalValue = null)
+ {
+ new TextWriterTokenWriter(new TextOutputWriter(output)).WritePrimitiveValue(value, literalValue);
+ }
+
+ public override void WritePrimitiveType(string type)
+ {
+ output.Write(type);
+ if (type == "new") {
+ output.Write("()");
+ }
+ }
+
+ Stack<TextLocation> startLocations = new Stack<TextLocation>();
+ Stack<MethodDebugSymbols> symbolsStack = new Stack<MethodDebugSymbols>();
+
+ public override void StartNode(AstNode node)
+ {
+ if (nodeStack.Count == 0) {
+ if (IsUsingDeclaration(node)) {
+ firstUsingDeclaration = !IsUsingDeclaration(node.PrevSibling);
+ lastUsingDeclaration = !IsUsingDeclaration(node.NextSibling);
+ } else {
+ firstUsingDeclaration = false;
+ lastUsingDeclaration = false;
+ }
+ }
+ nodeStack.Push(node);
+ startLocations.Push(output.Location);
+
+ if (node is EntityDeclaration && node.Annotation<MemberReference>() != null && node.GetChildByRole(Roles.Identifier).IsNull)
+ output.WriteDefinition("", node.Annotation<MemberReference>(), false);
+
+ if (node.Annotation<MethodDebugSymbols>() != null) {
+ symbolsStack.Push(node.Annotation<MethodDebugSymbols>());
+ symbolsStack.Peek().StartLocation = startLocations.Peek();
+ }
+ }
+
+ bool IsUsingDeclaration(AstNode node)
+ {
+ return node is UsingDeclaration || node is UsingAliasDeclaration;
+ }
+
+ public override void EndNode(AstNode node)
+ {
+ if (nodeStack.Pop() != node)
+ throw new InvalidOperationException();
+
+ var startLocation = startLocations.Pop();
+
+ // code mappings
+ var ranges = node.Annotation<List<ILRange>>();
+ if (symbolsStack.Count > 0 && ranges != null && ranges.Count > 0) {
+ // Ignore the newline which was printed at the end of the statement
+ TextLocation endLocation = (node is Statement) ? (lastEndOfLine ?? output.Location) : output.Location;
+ symbolsStack.Peek().SequencePoints.Add(
+ new SequencePoint() {
+ ILRanges = ILRange.OrderAndJoin(ranges).ToArray(),
+ StartLocation = startLocation,
+ EndLocation = endLocation
+ });
+ }
+
+ if (node.Annotation<MethodDebugSymbols>() != null) {
+ symbolsStack.Peek().EndLocation = output.Location;
+ output.AddDebugSymbols(symbolsStack.Pop());
+ }
+ }
+
+ static bool IsDefinition(AstNode node)
+ {
+ return node is EntityDeclaration
+ || (node is VariableInitializer && node.Parent is FieldDeclaration)
+ || node is FixedVariableInitializer;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs b/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs
new file mode 100644
index 00000000..dc018eb1
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs
@@ -0,0 +1,368 @@
+// 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.Linq;
+using ICSharpCode.Decompiler.ILAst;
+using ICSharpCode.NRefactory.CSharp;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ /// <summary>
+ /// Add checked/unchecked blocks.
+ /// </summary>
+ public class AddCheckedBlocks : IAstTransform
+ {
+ #region Annotation
+ sealed class CheckedUncheckedAnnotation {
+ /// <summary>
+ /// true=checked, false=unchecked
+ /// </summary>
+ public bool IsChecked;
+ }
+
+ public static readonly object CheckedAnnotation = new CheckedUncheckedAnnotation { IsChecked = true };
+ public static readonly object UncheckedAnnotation = new CheckedUncheckedAnnotation { IsChecked = false };
+ #endregion
+
+ /*
+ We treat placing checked/unchecked blocks as an optimization problem, with the following goals:
+ 1. Use minimum number of checked blocks+expressions
+ 2. Prefer checked expressions over checked blocks
+ 3. Make the scope of checked expressions as small as possible
+ 4. Open checked blocks as late as possible, and close checked blocks as late as possible
+ (where goal 1 has the highest priority)
+
+ Goal 4a (open checked blocks as late as possible) is necessary so that we don't move variable declarations
+ into checked blocks, as the variable might still be used after the checked block.
+ (this could cause DeclareVariables to omit the variable declaration, producing incorrect code)
+ Goal 4b (close checked blocks as late as possible) makes the code look nicer in this case:
+ checked {
+ int c = a + b;
+ int r = a + c;
+ return r;
+ }
+ If the checked block was closed as early as possible, the variable r would have to be declared outside
+ (this would work, but look badly)
+ */
+
+ #region struct Cost
+ struct Cost
+ {
+ // highest possible cost so that the Blocks+Expressions addition doesn't overflow
+ public static readonly Cost Infinite = new Cost(0x3fffffff, 0x3fffffff);
+
+ public readonly int Blocks;
+ public readonly int Expressions;
+
+ public Cost(int blocks, int expressions)
+ {
+ Blocks = blocks;
+ Expressions = expressions;
+ }
+
+ public static bool operator <(Cost a, Cost b)
+ {
+ return a.Blocks + a.Expressions < b.Blocks + b.Expressions
+ || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks < b.Blocks;
+ }
+
+ public static bool operator >(Cost a, Cost b)
+ {
+ return a.Blocks + a.Expressions > b.Blocks + b.Expressions
+ || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks > b.Blocks;
+ }
+
+ public static bool operator <=(Cost a, Cost b)
+ {
+ return a.Blocks + a.Expressions < b.Blocks + b.Expressions
+ || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks <= b.Blocks;
+ }
+
+ public static bool operator >=(Cost a, Cost b)
+ {
+ return a.Blocks + a.Expressions > b.Blocks + b.Expressions
+ || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks >= b.Blocks;
+ }
+
+ public static Cost operator +(Cost a, Cost b)
+ {
+ return new Cost(a.Blocks + b.Blocks, a.Expressions + b.Expressions);
+ }
+
+ public override string ToString()
+ {
+ return string.Format("[{0} + {1}]", Blocks, Expressions);
+ }
+ }
+ #endregion
+
+ #region class InsertedNode
+ /// <summary>
+ /// Holds the blocks and expressions that should be inserted
+ /// </summary>
+ abstract class InsertedNode
+ {
+ public static InsertedNode operator +(InsertedNode a, InsertedNode b)
+ {
+ if (a == null)
+ return b;
+ if (b == null)
+ return a;
+ return new InsertedNodeList(a, b);
+ }
+
+ public abstract void Insert();
+ }
+
+ class InsertedNodeList : InsertedNode
+ {
+ readonly InsertedNode child1, child2;
+
+ public InsertedNodeList(InsertedNode child1, InsertedNode child2)
+ {
+ this.child1 = child1;
+ this.child2 = child2;
+ }
+
+ public override void Insert()
+ {
+ child1.Insert();
+ child2.Insert();
+ }
+ }
+
+ class InsertedExpression : InsertedNode
+ {
+ readonly Expression expression;
+ readonly bool isChecked;
+
+ public InsertedExpression(Expression expression, bool isChecked)
+ {
+ this.expression = expression;
+ this.isChecked = isChecked;
+ }
+
+ public override void Insert()
+ {
+ if (isChecked)
+ expression.ReplaceWith(e => new CheckedExpression { Expression = e });
+ else
+ expression.ReplaceWith(e => new UncheckedExpression { Expression = e });
+ }
+ }
+
+ class ConvertCompoundAssignment : InsertedNode
+ {
+ readonly Expression expression;
+ readonly bool isChecked;
+
+ public ConvertCompoundAssignment(Expression expression, bool isChecked)
+ {
+ this.expression = expression;
+ this.isChecked = isChecked;
+ }
+
+ public override void Insert()
+ {
+ AssignmentExpression assign = expression.Annotation<ReplaceMethodCallsWithOperators.RestoreOriginalAssignOperatorAnnotation>().Restore(expression);
+ expression.ReplaceWith(assign);
+ if (isChecked)
+ assign.Right = new CheckedExpression { Expression = assign.Right.Detach() };
+ else
+ assign.Right = new UncheckedExpression { Expression = assign.Right.Detach() };
+ }
+ }
+
+ class InsertedBlock : InsertedNode
+ {
+ readonly Statement firstStatement; // inclusive
+ readonly Statement lastStatement; // exclusive
+ readonly bool isChecked;
+
+ public InsertedBlock(Statement firstStatement, Statement lastStatement, bool isChecked)
+ {
+ this.firstStatement = firstStatement;
+ this.lastStatement = lastStatement;
+ this.isChecked = isChecked;
+ }
+
+ public override void Insert()
+ {
+ BlockStatement newBlock = new BlockStatement();
+ // Move all statements except for the first
+ Statement next;
+ for (Statement stmt = firstStatement.GetNextStatement(); stmt != lastStatement; stmt = next) {
+ next = stmt.GetNextStatement();
+ newBlock.Add(stmt.Detach());
+ }
+ // Replace the first statement with the new (un)checked block
+ if (isChecked)
+ firstStatement.ReplaceWith(new CheckedStatement { Body = newBlock });
+ else
+ firstStatement.ReplaceWith(new UncheckedStatement { Body = newBlock });
+ // now also move the first node into the new block
+ newBlock.Statements.InsertAfter(null, firstStatement);
+ }
+ }
+ #endregion
+
+ #region class Result
+ /// <summary>
+ /// Holds the result of an insertion operation.
+ /// </summary>
+ class Result
+ {
+ public Cost CostInCheckedContext;
+ public InsertedNode NodesToInsertInCheckedContext;
+ public Cost CostInUncheckedContext;
+ public InsertedNode NodesToInsertInUncheckedContext;
+ }
+ #endregion
+
+ public void Run(AstNode node)
+ {
+ BlockStatement block = node as BlockStatement;
+ if (block == null) {
+ for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
+ Run(child);
+ }
+ } else {
+ Result r = GetResultFromBlock(block);
+ if (r.NodesToInsertInUncheckedContext != null)
+ r.NodesToInsertInUncheckedContext.Insert();
+ }
+ }
+
+ Result GetResultFromBlock(BlockStatement block)
+ {
+ // For a block, we are tracking 4 possibilities:
+ // a) context is checked, no unchecked block open
+ Cost costCheckedContext = new Cost(0, 0);
+ InsertedNode nodesCheckedContext = null;
+ // b) context is checked, an unchecked block is open
+ Cost costCheckedContextUncheckedBlockOpen = Cost.Infinite;
+ InsertedNode nodesCheckedContextUncheckedBlockOpen = null;
+ Statement uncheckedBlockStart = null;
+ // c) context is unchecked, no checked block open
+ Cost costUncheckedContext = new Cost(0, 0);
+ InsertedNode nodesUncheckedContext = null;
+ // d) context is unchecked, a checked block is open
+ Cost costUncheckedContextCheckedBlockOpen = Cost.Infinite;
+ InsertedNode nodesUncheckedContextCheckedBlockOpen = null;
+ Statement checkedBlockStart = null;
+
+ Statement statement = block.Statements.FirstOrDefault();
+ while (true) {
+ // Blocks can be closed 'for free'. We use '<=' so that blocks are closed as late as possible (goal 4b)
+ if (costCheckedContextUncheckedBlockOpen <= costCheckedContext) {
+ costCheckedContext = costCheckedContextUncheckedBlockOpen;
+ nodesCheckedContext = nodesCheckedContextUncheckedBlockOpen + new InsertedBlock(uncheckedBlockStart, statement, false);
+ }
+ if (costUncheckedContextCheckedBlockOpen <= costUncheckedContext) {
+ costUncheckedContext = costUncheckedContextCheckedBlockOpen;
+ nodesUncheckedContext = nodesUncheckedContextCheckedBlockOpen + new InsertedBlock(checkedBlockStart, statement, true);
+ }
+ if (statement == null)
+ break;
+ // Now try opening blocks. We use '<=' so that blocks are opened as late as possible. (goal 4a)
+ if (costCheckedContext + new Cost(1, 0) <= costCheckedContextUncheckedBlockOpen) {
+ costCheckedContextUncheckedBlockOpen = costCheckedContext + new Cost(1, 0);
+ nodesCheckedContextUncheckedBlockOpen = nodesCheckedContext;
+ uncheckedBlockStart = statement;
+ }
+ if (costUncheckedContext + new Cost(1, 0) <= costUncheckedContextCheckedBlockOpen) {
+ costUncheckedContextCheckedBlockOpen = costUncheckedContext + new Cost(1, 0);
+ nodesUncheckedContextCheckedBlockOpen = nodesUncheckedContext;
+ checkedBlockStart = statement;
+ }
+ // Now handle the statement
+ Result stmtResult = GetResult(statement);
+
+ costCheckedContext += stmtResult.CostInCheckedContext;
+ nodesCheckedContext += stmtResult.NodesToInsertInCheckedContext;
+ costCheckedContextUncheckedBlockOpen += stmtResult.CostInUncheckedContext;
+ nodesCheckedContextUncheckedBlockOpen += stmtResult.NodesToInsertInUncheckedContext;
+ costUncheckedContext += stmtResult.CostInUncheckedContext;
+ nodesUncheckedContext += stmtResult.NodesToInsertInUncheckedContext;
+ costUncheckedContextCheckedBlockOpen += stmtResult.CostInCheckedContext;
+ nodesUncheckedContextCheckedBlockOpen += stmtResult.NodesToInsertInCheckedContext;
+
+ statement = statement.GetNextStatement();
+ }
+
+ return new Result {
+ CostInCheckedContext = costCheckedContext, NodesToInsertInCheckedContext = nodesCheckedContext,
+ CostInUncheckedContext = costUncheckedContext, NodesToInsertInUncheckedContext = nodesUncheckedContext
+ };
+ }
+
+ Result GetResult(AstNode node)
+ {
+ if (node is BlockStatement)
+ return GetResultFromBlock((BlockStatement)node);
+ Result result = new Result();
+ for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
+ Result childResult = GetResult(child);
+ result.CostInCheckedContext += childResult.CostInCheckedContext;
+ result.NodesToInsertInCheckedContext += childResult.NodesToInsertInCheckedContext;
+ result.CostInUncheckedContext += childResult.CostInUncheckedContext;
+ result.NodesToInsertInUncheckedContext += childResult.NodesToInsertInUncheckedContext;
+ }
+ Expression expr = node as Expression;
+ if (expr != null) {
+ CheckedUncheckedAnnotation annotation = expr.Annotation<CheckedUncheckedAnnotation>();
+ if (annotation != null) {
+ // If the annotation requires this node to be in a specific context, add a huge cost to the other context
+ // That huge cost gives us the option to ignore a required checked/unchecked expression when there wouldn't be any
+ // solution otherwise. (e.g. "for (checked(M().x += 1); true; unchecked(M().x += 2)) {}")
+ if (annotation.IsChecked)
+ result.CostInUncheckedContext += new Cost(10000, 0);
+ else
+ result.CostInCheckedContext += new Cost(10000, 0);
+ }
+ // Embed this node in an checked/unchecked expression:
+ if (expr.Parent is ExpressionStatement) {
+ // We cannot use checked/unchecked for top-level-expressions.
+ // However, we could try converting a compound assignment (checked(a+=b);) or unary operator (checked(a++);)
+ // back to its old form.
+ if (expr.Annotation<ReplaceMethodCallsWithOperators.RestoreOriginalAssignOperatorAnnotation>() != null) {
+ // We use '<' so that expressions are introduced on the deepest level possible (goal 3)
+ if (result.CostInCheckedContext + new Cost(1, 1) < result.CostInUncheckedContext) {
+ result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(1, 1);
+ result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new ConvertCompoundAssignment(expr, true);
+ } else if (result.CostInUncheckedContext + new Cost(1, 1) < result.CostInCheckedContext) {
+ result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(1, 1);
+ result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new ConvertCompoundAssignment(expr, false);
+ }
+ }
+ } else if (expr.Role.IsValid(Expression.Null)) {
+ // We use '<' so that expressions are introduced on the deepest level possible (goal 3)
+ if (result.CostInCheckedContext + new Cost(0, 1) < result.CostInUncheckedContext) {
+ result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(0, 1);
+ result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new InsertedExpression(expr, true);
+ } else if (result.CostInUncheckedContext + new Cost(0, 1) < result.CostInCheckedContext) {
+ result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(0, 1);
+ result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new InsertedExpression(expr, false);
+ }
+ }
+ }
+ return result;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs b/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs
new file mode 100644
index 00000000..a0d1ca8c
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs
@@ -0,0 +1,179 @@
+// 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.Linq;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.PatternMatching;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ /// <summary>
+ /// Combines query expressions and removes transparent identifiers.
+ /// </summary>
+ public class CombineQueryExpressions : IAstTransform
+ {
+ readonly DecompilerContext context;
+
+ public CombineQueryExpressions(DecompilerContext context)
+ {
+ this.context = context;
+ }
+
+ public void Run(AstNode compilationUnit)
+ {
+ if (!context.Settings.QueryExpressions)
+ return;
+ CombineQueries(compilationUnit);
+ }
+
+ static readonly InvocationExpression castPattern = new InvocationExpression {
+ Target = new MemberReferenceExpression {
+ Target = new AnyNode("inExpr"),
+ MemberName = "Cast",
+ TypeArguments = { new AnyNode("targetType") }
+ }};
+
+ void CombineQueries(AstNode node)
+ {
+ for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
+ CombineQueries(child);
+ }
+ QueryExpression query = node as QueryExpression;
+ if (query != null) {
+ QueryFromClause fromClause = (QueryFromClause)query.Clauses.First();
+ QueryExpression innerQuery = fromClause.Expression as QueryExpression;
+ if (innerQuery != null) {
+ if (TryRemoveTransparentIdentifier(query, fromClause, innerQuery)) {
+ RemoveTransparentIdentifierReferences(query);
+ } else {
+ QueryContinuationClause continuation = new QueryContinuationClause();
+ continuation.PrecedingQuery = innerQuery.Detach();
+ continuation.Identifier = fromClause.Identifier;
+ fromClause.ReplaceWith(continuation);
+ }
+ } else {
+ Match m = castPattern.Match(fromClause.Expression);
+ if (m.Success) {
+ fromClause.Type = m.Get<AstType>("targetType").Single().Detach();
+ fromClause.Expression = m.Get<Expression>("inExpr").Single().Detach();
+ }
+ }
+ }
+ }
+
+ static readonly QuerySelectClause selectTransparentIdentifierPattern = new QuerySelectClause {
+ Expression = new Choice {
+ new AnonymousTypeCreateExpression {
+ Initializers = {
+ new NamedExpression {
+ Name = Pattern.AnyString,
+ Expression = new IdentifierExpression(Pattern.AnyString)
+ }.WithName("nae1"),
+ new NamedExpression {
+ Name = Pattern.AnyString,
+ Expression = new AnyNode("nae2Expr")
+ }.WithName("nae2")
+ }
+ },
+ new AnonymousTypeCreateExpression {
+ Initializers = {
+ new NamedNode("identifier", new IdentifierExpression(Pattern.AnyString)),
+ new AnyNode("nae2Expr")
+ }
+ }
+ }};
+
+ bool IsTransparentIdentifier(string identifier)
+ {
+ return identifier.StartsWith("<>", StringComparison.Ordinal) && identifier.Contains("TransparentIdentifier");
+ }
+
+ bool TryRemoveTransparentIdentifier(QueryExpression query, QueryFromClause fromClause, QueryExpression innerQuery)
+ {
+ if (!IsTransparentIdentifier(fromClause.Identifier))
+ return false;
+ Match match = selectTransparentIdentifierPattern.Match(innerQuery.Clauses.Last());
+ if (!match.Success)
+ return false;
+ QuerySelectClause selectClause = (QuerySelectClause)innerQuery.Clauses.Last();
+ NamedExpression nae1 = match.Get<NamedExpression>("nae1").SingleOrDefault();
+ NamedExpression nae2 = match.Get<NamedExpression>("nae2").SingleOrDefault();
+ if (nae1 != null && nae1.Name != ((IdentifierExpression)nae1.Expression).Identifier)
+ return false;
+ Expression nae2Expr = match.Get<Expression>("nae2Expr").Single();
+ IdentifierExpression nae2IdentExpr = nae2Expr as IdentifierExpression;
+ if (nae2IdentExpr != null && (nae2 == null || nae2.Name == nae2IdentExpr.Identifier)) {
+ // from * in (from x in ... select new { x = x, y = y }) ...
+ // =>
+ // from x in ... ...
+ fromClause.Remove();
+ selectClause.Remove();
+ // Move clauses from innerQuery to query
+ QueryClause insertionPos = null;
+ foreach (var clause in innerQuery.Clauses) {
+ query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach());
+ }
+ } else {
+ // from * in (from x in ... select new { x = x, y = expr }) ...
+ // =>
+ // from x in ... let y = expr ...
+ fromClause.Remove();
+ selectClause.Remove();
+ // Move clauses from innerQuery to query
+ QueryClause insertionPos = null;
+ foreach (var clause in innerQuery.Clauses) {
+ query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach());
+ }
+ string ident;
+ if (nae2 != null)
+ ident = nae2.Name;
+ else if (nae2Expr is IdentifierExpression)
+ ident = ((IdentifierExpression)nae2Expr).Identifier;
+ else if (nae2Expr is MemberReferenceExpression)
+ ident = ((MemberReferenceExpression)nae2Expr).MemberName;
+ else
+ throw new InvalidOperationException("Could not infer name from initializer in AnonymousTypeCreateExpression");
+ query.Clauses.InsertAfter(insertionPos, new QueryLetClause { Identifier = ident, Expression = nae2Expr.Detach() });
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Removes all occurrences of transparent identifiers
+ /// </summary>
+ void RemoveTransparentIdentifierReferences(AstNode node)
+ {
+ foreach (AstNode child in node.Children) {
+ RemoveTransparentIdentifierReferences(child);
+ }
+ MemberReferenceExpression mre = node as MemberReferenceExpression;
+ if (mre != null) {
+ IdentifierExpression ident = mre.Target as IdentifierExpression;
+ if (ident != null && IsTransparentIdentifier(ident.Identifier)) {
+ IdentifierExpression newIdent = new IdentifierExpression(mre.MemberName);
+ mre.TypeArguments.MoveTo(newIdent.TypeArguments);
+ newIdent.CopyAnnotationsFrom(mre);
+ newIdent.RemoveAnnotations<PropertyDeclaration>(); // remove the reference to the property of the anonymous type
+ mre.ReplaceWith(newIdent);
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs b/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs
new file mode 100644
index 00000000..1d1f3d08
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs
@@ -0,0 +1,111 @@
+// 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.Diagnostics;
+using ICSharpCode.NRefactory.CSharp;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ /// <summary>
+ /// Base class for AST visitors that need the current type/method context info.
+ /// </summary>
+ public abstract class ContextTrackingVisitor<TResult> : DepthFirstAstVisitor<object, TResult>, IAstTransform
+ {
+ protected readonly DecompilerContext context;
+
+ protected ContextTrackingVisitor(DecompilerContext context)
+ {
+ if (context == null)
+ throw new ArgumentNullException("context");
+ this.context = context;
+ }
+
+ public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
+ {
+ TypeDefinition oldType = context.CurrentType;
+ try {
+ context.CurrentType = typeDeclaration.Annotation<TypeDefinition>();
+ return base.VisitTypeDeclaration(typeDeclaration, data);
+ } finally {
+ context.CurrentType = oldType;
+ }
+ }
+
+ public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
+ {
+ Debug.Assert(context.CurrentMethod == null);
+ try {
+ context.CurrentMethod = methodDeclaration.Annotation<MethodDefinition>();
+ return base.VisitMethodDeclaration(methodDeclaration, data);
+ } finally {
+ context.CurrentMethod = null;
+ }
+ }
+
+ public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
+ {
+ Debug.Assert(context.CurrentMethod == null);
+ try {
+ context.CurrentMethod = constructorDeclaration.Annotation<MethodDefinition>();
+ return base.VisitConstructorDeclaration(constructorDeclaration, data);
+ } finally {
+ context.CurrentMethod = null;
+ }
+ }
+
+ public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, object data)
+ {
+ Debug.Assert(context.CurrentMethod == null);
+ try {
+ context.CurrentMethod = destructorDeclaration.Annotation<MethodDefinition>();
+ return base.VisitDestructorDeclaration(destructorDeclaration, data);
+ } finally {
+ context.CurrentMethod = null;
+ }
+ }
+
+ public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data)
+ {
+ Debug.Assert(context.CurrentMethod == null);
+ try {
+ context.CurrentMethod = operatorDeclaration.Annotation<MethodDefinition>();
+ return base.VisitOperatorDeclaration(operatorDeclaration, data);
+ } finally {
+ context.CurrentMethod = null;
+ }
+ }
+
+ public override TResult VisitAccessor(Accessor accessor, object data)
+ {
+ Debug.Assert(context.CurrentMethod == null);
+ try {
+ context.CurrentMethod = accessor.Annotation<MethodDefinition>();
+ return base.VisitAccessor(accessor, data);
+ } finally {
+ context.CurrentMethod = null;
+ }
+ }
+
+ void IAstTransform.Run(AstNode node)
+ {
+ node.AcceptVisitor(this, null);
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs
new file mode 100644
index 00000000..36811c18
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs
@@ -0,0 +1,183 @@
+// 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.Generic;
+using System.Linq;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.PatternMatching;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ /// <summary>
+ /// If the first element of a constructor is a chained constructor call, convert it into a constructor initializer.
+ /// </summary>
+ public class ConvertConstructorCallIntoInitializer : DepthFirstAstVisitor<object, object>, IAstTransform
+ {
+ public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
+ {
+ ExpressionStatement stmt = constructorDeclaration.Body.Statements.FirstOrDefault() as ExpressionStatement;
+ if (stmt == null)
+ return null;
+ InvocationExpression invocation = stmt.Expression as InvocationExpression;
+ if (invocation == null)
+ return null;
+ MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression;
+ if (mre != null && mre.MemberName == ".ctor") {
+ ConstructorInitializer ci = new ConstructorInitializer();
+ if (mre.Target is ThisReferenceExpression)
+ ci.ConstructorInitializerType = ConstructorInitializerType.This;
+ else if (mre.Target is BaseReferenceExpression)
+ ci.ConstructorInitializerType = ConstructorInitializerType.Base;
+ else
+ return null;
+ // Move arguments from invocation to initializer:
+ invocation.Arguments.MoveTo(ci.Arguments);
+ // Add the initializer: (unless it is the default 'base()')
+ if (!(ci.ConstructorInitializerType == ConstructorInitializerType.Base && ci.Arguments.Count == 0))
+ constructorDeclaration.Initializer = ci.WithAnnotation(invocation.Annotation<MethodReference>());
+ // Remove the statement:
+ stmt.Remove();
+ }
+ return null;
+ }
+
+ static readonly ExpressionStatement fieldInitializerPattern = new ExpressionStatement {
+ Expression = new AssignmentExpression {
+ Left = new NamedNode("fieldAccess", new MemberReferenceExpression {
+ Target = new ThisReferenceExpression(),
+ MemberName = Pattern.AnyString
+ }),
+ Operator = AssignmentOperatorType.Assign,
+ Right = new AnyNode("initializer")
+ }
+ };
+
+ static readonly AstNode thisCallPattern = new ExpressionStatement(new ThisReferenceExpression().Invoke(".ctor", new Repeat(new AnyNode())));
+
+ public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
+ {
+ // Handle initializers on instance fields
+ HandleInstanceFieldInitializers(typeDeclaration.Members);
+
+ // Now convert base constructor calls to initializers:
+ base.VisitTypeDeclaration(typeDeclaration, data);
+
+ // Remove single empty constructor:
+ RemoveSingleEmptyConstructor(typeDeclaration);
+
+ // Handle initializers on static fields:
+ HandleStaticFieldInitializers(typeDeclaration.Members);
+ return null;
+ }
+
+ void HandleInstanceFieldInitializers(IEnumerable<AstNode> members)
+ {
+ var instanceCtors = members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
+ var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray();
+ if (instanceCtorsNotChainingWithThis.Length > 0) {
+ MethodDefinition ctorMethodDef = instanceCtorsNotChainingWithThis[0].Annotation<MethodDefinition>();
+ if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsValueType)
+ return;
+
+ // Recognize field initializers:
+ // Convert first statement in all ctors (if all ctors have the same statement) into a field initializer.
+ bool allSame;
+ do {
+ Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault());
+ if (!m.Success)
+ break;
+
+ FieldDefinition fieldDef = m.Get<AstNode>("fieldAccess").Single().Annotation<FieldReference>().ResolveWithinSameModule();
+ if (fieldDef == null)
+ break;
+ AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
+ if (fieldOrEventDecl == null)
+ break;
+ Expression initializer = m.Get<Expression>("initializer").Single();
+ // 'this'/'base' cannot be used in field initializers
+ if (initializer.DescendantsAndSelf.Any(n => n is ThisReferenceExpression || n is BaseReferenceExpression))
+ break;
+
+ allSame = true;
+ for (int i = 1; i < instanceCtorsNotChainingWithThis.Length; i++) {
+ if (!instanceCtors[0].Body.First().IsMatch(instanceCtorsNotChainingWithThis[i].Body.FirstOrDefault()))
+ allSame = false;
+ }
+ if (allSame) {
+ foreach (var ctor in instanceCtorsNotChainingWithThis)
+ ctor.Body.First().Remove();
+ fieldOrEventDecl.GetChildrenByRole(Roles.Variable).Single().Initializer = initializer.Detach();
+ }
+ } while (allSame);
+ }
+ }
+
+ void RemoveSingleEmptyConstructor(TypeDeclaration typeDeclaration)
+ {
+ var instanceCtors = typeDeclaration.Members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
+ if (instanceCtors.Length == 1) {
+ ConstructorDeclaration emptyCtor = new ConstructorDeclaration();
+ emptyCtor.Modifiers = ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public);
+ emptyCtor.Body = new BlockStatement();
+ if (emptyCtor.IsMatch(instanceCtors[0]))
+ instanceCtors[0].Remove();
+ }
+ }
+
+ void HandleStaticFieldInitializers(IEnumerable<AstNode> members)
+ {
+ // Convert static constructor into field initializers if the class is BeforeFieldInit
+ var staticCtor = members.OfType<ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static);
+ if (staticCtor != null) {
+ MethodDefinition ctorMethodDef = staticCtor.Annotation<MethodDefinition>();
+ if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsBeforeFieldInit) {
+ while (true) {
+ ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement;
+ if (es == null)
+ break;
+ AssignmentExpression assignment = es.Expression as AssignmentExpression;
+ if (assignment == null || assignment.Operator != AssignmentOperatorType.Assign)
+ break;
+ FieldDefinition fieldDef = assignment.Left.Annotation<FieldReference>().ResolveWithinSameModule();
+ if (fieldDef == null || !fieldDef.IsStatic)
+ break;
+ FieldDeclaration fieldDecl = members.OfType<FieldDeclaration>().FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
+ if (fieldDecl == null)
+ break;
+ fieldDecl.Variables.Single().Initializer = assignment.Right.Detach();
+ es.Remove();
+ }
+ if (staticCtor.Body.Statements.Count == 0)
+ staticCtor.Remove();
+ }
+ }
+ }
+
+ void IAstTransform.Run(AstNode node)
+ {
+ // If we're viewing some set of members (fields are direct children of CompilationUnit),
+ // we also need to handle those:
+ HandleInstanceFieldInitializers(node.Children);
+ HandleStaticFieldInitializers(node.Children);
+
+ node.AcceptVisitor(this, null);
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/CustomPatterns.cs b/ICSharpCode.Decompiler/Ast/Transforms/CustomPatterns.cs
new file mode 100644
index 00000000..b80c56af
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/CustomPatterns.cs
@@ -0,0 +1,109 @@
+// 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.Linq;
+using System.Reflection;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.PatternMatching;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ internal sealed class TypePattern : Pattern
+ {
+ readonly string ns;
+ readonly string name;
+
+ public TypePattern(Type type)
+ {
+ ns = type.Namespace;
+ name = type.Name;
+ }
+
+ public override bool DoMatch(INode other, Match match)
+ {
+ ComposedType ct = other as ComposedType;
+ AstType o;
+ if (ct != null && !ct.HasNullableSpecifier && ct.PointerRank == 0 && !ct.ArraySpecifiers.Any()) {
+ // Special case: ILSpy sometimes produces a ComposedType but then removed all array specifiers
+ // from it. In that case, we need to look at the base type for the annotations.
+ o = ct.BaseType;
+ } else {
+ o = other as AstType;
+ if (o == null)
+ return false;
+ }
+ TypeReference tr = o.Annotation<TypeReference>();
+ return tr != null && tr.Namespace == ns && tr.Name == name;
+ }
+
+ public override string ToString()
+ {
+ return name;
+ }
+ }
+
+ internal sealed class LdTokenPattern : Pattern
+ {
+ AnyNode childNode;
+
+ public LdTokenPattern(string groupName)
+ {
+ childNode = new AnyNode(groupName);
+ }
+
+ public override bool DoMatch(INode other, Match match)
+ {
+ InvocationExpression ie = other as InvocationExpression;
+ if (ie != null && ie.Annotation<LdTokenAnnotation>() != null && ie.Arguments.Count == 1) {
+ return childNode.DoMatch(ie.Arguments.Single(), match);
+ }
+ return false;
+ }
+
+ public override string ToString()
+ {
+ return "ldtoken(...)";
+ }
+ }
+
+ /// <summary>
+ /// typeof-Pattern that applies on the expanded form of typeof (prior to ReplaceMethodCallsWithOperators)
+ /// </summary>
+ internal sealed class TypeOfPattern : Pattern
+ {
+ INode childNode;
+
+ public TypeOfPattern(string groupName)
+ {
+ childNode = new TypePattern(typeof(Type)).ToType().Invoke(
+ "GetTypeFromHandle", new TypeOfExpression(new AnyNode(groupName)).Member("TypeHandle"));
+ }
+
+ public override bool DoMatch(INode other, Match match)
+ {
+ return childNode.DoMatch(other, match);
+ }
+
+ public override string ToString()
+ {
+ return "typeof(...)";
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs
new file mode 100644
index 00000000..298682af
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs
@@ -0,0 +1,58 @@
+// 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 ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.PatternMatching;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ /// <summary>
+ /// Transforms decimal constant fields.
+ /// </summary>
+ public class DecimalConstantTransform : DepthFirstAstVisitor<object, object>, IAstTransform
+ {
+ static readonly PrimitiveType decimalType = new PrimitiveType("decimal");
+
+ public override object VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data)
+ {
+ const Modifiers staticReadOnly = Modifiers.Static | Modifiers.Readonly;
+ if ((fieldDeclaration.Modifiers & staticReadOnly) == staticReadOnly && decimalType.IsMatch(fieldDeclaration.ReturnType)) {
+ foreach (var attributeSection in fieldDeclaration.Attributes) {
+ foreach (var attribute in attributeSection.Attributes) {
+ TypeReference tr = attribute.Type.Annotation<TypeReference>();
+ if (tr != null && tr.Name == "DecimalConstantAttribute" && tr.Namespace == "System.Runtime.CompilerServices") {
+ attribute.Remove();
+ if (attributeSection.Attributes.Count == 0)
+ attributeSection.Remove();
+ fieldDeclaration.Modifiers = (fieldDeclaration.Modifiers & ~staticReadOnly) | Modifiers.Const;
+ return null;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public void Run(AstNode compilationUnit)
+ {
+ compilationUnit.AcceptVisitor(this, null);
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs
new file mode 100644
index 00000000..a27f30b4
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs
@@ -0,0 +1,368 @@
+// 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.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using ICSharpCode.Decompiler.ILAst;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.CSharp.Analysis;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ /// <summary>
+ /// Moves variable declarations to improved positions.
+ /// </summary>
+ public class DeclareVariables : IAstTransform
+ {
+ sealed class VariableToDeclare
+ {
+ public AstType Type;
+ public string Name;
+ public ILVariable ILVariable;
+
+ public AssignmentExpression ReplacedAssignment;
+ public Statement InsertionPoint;
+ }
+
+ readonly CancellationToken cancellationToken;
+ List<VariableToDeclare> variablesToDeclare = new List<VariableToDeclare>();
+
+ public DeclareVariables(DecompilerContext context)
+ {
+ cancellationToken = context.CancellationToken;
+ }
+
+ public void Run(AstNode node)
+ {
+ Run(node, null);
+ // Declare all the variables at the end, after all the logic has run.
+ // This is done so that definite assignment analysis can work on a single representation and doesn't have to be updated
+ // when we change the AST.
+ foreach (var v in variablesToDeclare) {
+ if (v.ReplacedAssignment == null) {
+ BlockStatement block = (BlockStatement)v.InsertionPoint.Parent;
+ var decl = new VariableDeclarationStatement((AstType)v.Type.Clone(), v.Name);
+ if (v.ILVariable != null)
+ decl.Variables.Single().AddAnnotation(v.ILVariable);
+ block.Statements.InsertBefore(
+ v.InsertionPoint,
+ decl);
+ }
+ }
+ // First do all the insertions, then do all the replacements. This is necessary because a replacement might remove our reference point from the AST.
+ foreach (var v in variablesToDeclare) {
+ if (v.ReplacedAssignment != null) {
+ // We clone the right expression so that it doesn't get removed from the old ExpressionStatement,
+ // which might be still in use by the definite assignment graph.
+ VariableInitializer initializer = new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()).CopyAnnotationsFrom(v.ReplacedAssignment).WithAnnotation(v.ILVariable);
+ VariableDeclarationStatement varDecl = new VariableDeclarationStatement {
+ Type = (AstType)v.Type.Clone(),
+ Variables = { initializer }
+ };
+ ExpressionStatement es = v.ReplacedAssignment.Parent as ExpressionStatement;
+ if (es != null) {
+ // Note: if this crashes with 'Cannot replace the root node', check whether two variables were assigned the same name
+ es.ReplaceWith(varDecl.CopyAnnotationsFrom(es));
+ } else {
+ v.ReplacedAssignment.ReplaceWith(varDecl);
+ }
+ }
+ }
+ variablesToDeclare = null;
+ }
+
+ void Run(AstNode node, DefiniteAssignmentAnalysis daa)
+ {
+ BlockStatement block = node as BlockStatement;
+ if (block != null) {
+ var variables = block.Statements.TakeWhile(stmt => stmt is VariableDeclarationStatement)
+ .Cast<VariableDeclarationStatement>().ToList();
+ if (variables.Count > 0) {
+ // remove old variable declarations:
+ foreach (VariableDeclarationStatement varDecl in variables) {
+ Debug.Assert(varDecl.Variables.Single().Initializer.IsNull);
+ varDecl.Remove();
+ }
+ if (daa == null) {
+ // If possible, reuse the DefiniteAssignmentAnalysis that was created for the parent block
+ daa = new DefiniteAssignmentAnalysis(block, cancellationToken);
+ }
+ foreach (VariableDeclarationStatement varDecl in variables) {
+ VariableInitializer initializer = varDecl.Variables.Single();
+ string variableName = initializer.Name;
+ ILVariable v = initializer.Annotation<ILVariable>();
+ bool allowPassIntoLoops = initializer.Annotation<DelegateConstruction.CapturedVariableAnnotation>() == null;
+ DeclareVariableInBlock(daa, block, varDecl.Type, variableName, v, allowPassIntoLoops);
+ }
+ }
+ }
+ for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
+ Run(child, daa);
+ }
+ }
+
+ void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, ILVariable v, bool allowPassIntoLoops)
+ {
+ // declarationPoint: The point where the variable would be declared, if we decide to declare it in this block
+ Statement declarationPoint = null;
+ // Check whether we can move down the variable into the sub-blocks
+ bool canMoveVariableIntoSubBlocks = FindDeclarationPoint(daa, variableName, allowPassIntoLoops, block, out declarationPoint);
+ if (declarationPoint == null) {
+ // The variable isn't used at all
+ return;
+ }
+ if (canMoveVariableIntoSubBlocks) {
+ // Declare the variable within the sub-blocks
+ foreach (Statement stmt in block.Statements) {
+ ForStatement forStmt = stmt as ForStatement;
+ if (forStmt != null && forStmt.Initializers.Count == 1) {
+ // handle the special case of moving a variable into the for initializer
+ if (TryConvertAssignmentExpressionIntoVariableDeclaration(forStmt.Initializers.Single(), type, variableName))
+ continue;
+ }
+ UsingStatement usingStmt = stmt as UsingStatement;
+ if (usingStmt != null && usingStmt.ResourceAcquisition is AssignmentExpression) {
+ // handle the special case of moving a variable into a using statement
+ if (TryConvertAssignmentExpressionIntoVariableDeclaration((Expression)usingStmt.ResourceAcquisition, type, variableName))
+ continue;
+ }
+ IfElseStatement ies = stmt as IfElseStatement;
+ if (ies != null) {
+ foreach (var child in IfElseChainChildren(ies)) {
+ BlockStatement subBlock = child as BlockStatement;
+ if (subBlock != null)
+ DeclareVariableInBlock(daa, subBlock, type, variableName, v, allowPassIntoLoops);
+ }
+ continue;
+ }
+ foreach (AstNode child in stmt.Children) {
+ BlockStatement subBlock = child as BlockStatement;
+ if (subBlock != null) {
+ DeclareVariableInBlock(daa, subBlock, type, variableName, v, allowPassIntoLoops);
+ } else if (HasNestedBlocks(child)) {
+ foreach (BlockStatement nestedSubBlock in child.Children.OfType<BlockStatement>()) {
+ DeclareVariableInBlock(daa, nestedSubBlock, type, variableName, v, allowPassIntoLoops);
+ }
+ }
+ }
+ }
+ } else {
+ // Try converting an assignment expression into a VariableDeclarationStatement
+ if (!TryConvertAssignmentExpressionIntoVariableDeclaration(declarationPoint, type, variableName)) {
+ // Declare the variable in front of declarationPoint
+ variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ILVariable = v, InsertionPoint = declarationPoint });
+ }
+ }
+ }
+
+ bool TryConvertAssignmentExpressionIntoVariableDeclaration(Statement declarationPoint, AstType type, string variableName)
+ {
+ // convert the declarationPoint into a VariableDeclarationStatement
+ ExpressionStatement es = declarationPoint as ExpressionStatement;
+ if (es != null) {
+ return TryConvertAssignmentExpressionIntoVariableDeclaration(es.Expression, type, variableName);
+ }
+ return false;
+ }
+
+ bool TryConvertAssignmentExpressionIntoVariableDeclaration(Expression expression, AstType type, string variableName)
+ {
+ AssignmentExpression ae = expression as AssignmentExpression;
+ if (ae != null && ae.Operator == AssignmentOperatorType.Assign) {
+ IdentifierExpression ident = ae.Left as IdentifierExpression;
+ if (ident != null && ident.Identifier == variableName) {
+ variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ILVariable = ident.Annotation<ILVariable>(), ReplacedAssignment = ae });
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Finds the declaration point for the variable within the specified block.
+ /// </summary>
+ /// <param name="daa">
+ /// Definite assignment analysis, must be prepared for 'block' or one of its parents.
+ /// </param>
+ /// <param name="varDecl">The variable to declare</param>
+ /// <param name="block">The block in which the variable should be declared</param>
+ /// <param name="declarationPoint">
+ /// Output parameter: the first statement within 'block' where the variable needs to be declared.
+ /// </param>
+ /// <returns>
+ /// Returns whether it is possible to move the variable declaration into sub-blocks.
+ /// </returns>
+ public static bool FindDeclarationPoint(DefiniteAssignmentAnalysis daa, VariableDeclarationStatement varDecl, BlockStatement block, out Statement declarationPoint)
+ {
+ string variableName = varDecl.Variables.Single().Name;
+ bool allowPassIntoLoops = varDecl.Variables.Single().Annotation<DelegateConstruction.CapturedVariableAnnotation>() == null;
+ return FindDeclarationPoint(daa, variableName, allowPassIntoLoops, block, out declarationPoint);
+ }
+
+ static bool FindDeclarationPoint(DefiniteAssignmentAnalysis daa, string variableName, bool allowPassIntoLoops, BlockStatement block, out Statement declarationPoint)
+ {
+ // declarationPoint: The point where the variable would be declared, if we decide to declare it in this block
+ declarationPoint = null;
+ foreach (Statement stmt in block.Statements) {
+ if (UsesVariable(stmt, variableName)) {
+ if (declarationPoint == null)
+ declarationPoint = stmt;
+ if (!CanMoveVariableUseIntoSubBlock(stmt, variableName, allowPassIntoLoops)) {
+ // If it's not possible to move the variable use into a nested block,
+ // we need to declare the variable in this block
+ return false;
+ }
+ // If we can move the variable into the sub-block, we need to ensure that the remaining code
+ // does not use the value that was assigned by the first sub-block
+ Statement nextStatement = stmt.GetNextStatement();
+ if (nextStatement != null) {
+ // Analyze the range from the next statement to the end of the block
+ daa.SetAnalyzedRange(nextStatement, block);
+ daa.Analyze(variableName);
+ if (daa.UnassignedVariableUses.Count > 0) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ static bool CanMoveVariableUseIntoSubBlock(Statement stmt, string variableName, bool allowPassIntoLoops)
+ {
+ if (!allowPassIntoLoops && (stmt is ForStatement || stmt is ForeachStatement || stmt is DoWhileStatement || stmt is WhileStatement))
+ return false;
+
+ ForStatement forStatement = stmt as ForStatement;
+ if (forStatement != null && forStatement.Initializers.Count == 1) {
+ // for-statement is special case: we can move variable declarations into the initializer
+ ExpressionStatement es = forStatement.Initializers.Single() as ExpressionStatement;
+ if (es != null) {
+ AssignmentExpression ae = es.Expression as AssignmentExpression;
+ if (ae != null && ae.Operator == AssignmentOperatorType.Assign) {
+ IdentifierExpression ident = ae.Left as IdentifierExpression;
+ if (ident != null && ident.Identifier == variableName) {
+ return !UsesVariable(ae.Right, variableName);
+ }
+ }
+ }
+ }
+
+ UsingStatement usingStatement = stmt as UsingStatement;
+ if (usingStatement != null) {
+ // using-statement is special case: we can move variable declarations into the initializer
+ AssignmentExpression ae = usingStatement.ResourceAcquisition as AssignmentExpression;
+ if (ae != null && ae.Operator == AssignmentOperatorType.Assign) {
+ IdentifierExpression ident = ae.Left as IdentifierExpression;
+ if (ident != null && ident.Identifier == variableName) {
+ return !UsesVariable(ae.Right, variableName);
+ }
+ }
+ }
+
+ IfElseStatement ies = stmt as IfElseStatement;
+ if (ies != null) {
+ foreach (var child in IfElseChainChildren(ies)) {
+ if (!(child is BlockStatement) && UsesVariable(child, variableName))
+ return false;
+ }
+ return true;
+ }
+
+ // We can move the variable into a sub-block only if the variable is used in only that sub-block (and not in expressions such as the loop condition)
+ for (AstNode child = stmt.FirstChild; child != null; child = child.NextSibling) {
+ if (!(child is BlockStatement) && UsesVariable(child, variableName)) {
+ if (HasNestedBlocks(child)) {
+ // catch clauses/switch sections can contain nested blocks
+ for (AstNode grandchild = child.FirstChild; grandchild != null; grandchild = grandchild.NextSibling) {
+ if (!(grandchild is BlockStatement) && UsesVariable(grandchild, variableName))
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ static IEnumerable<AstNode> IfElseChainChildren(IfElseStatement ies)
+ {
+ IfElseStatement prev;
+ do {
+ yield return ies.Condition;
+ yield return ies.TrueStatement;
+ prev = ies;
+ ies = ies.FalseStatement as IfElseStatement;
+ } while (ies != null);
+ if (!prev.FalseStatement.IsNull)
+ yield return prev.FalseStatement;
+ }
+
+ static bool HasNestedBlocks(AstNode node)
+ {
+ return node is CatchClause || node is SwitchSection;
+ }
+
+ static bool UsesVariable(AstNode node, string variableName)
+ {
+ IdentifierExpression ie = node as IdentifierExpression;
+ if (ie != null && ie.Identifier == variableName)
+ return true;
+
+ FixedStatement fixedStatement = node as FixedStatement;
+ if (fixedStatement != null) {
+ foreach (VariableInitializer v in fixedStatement.Variables) {
+ if (v.Name == variableName)
+ return false; // no need to introduce the variable here
+ }
+ }
+
+ ForeachStatement foreachStatement = node as ForeachStatement;
+ if (foreachStatement != null) {
+ if (foreachStatement.VariableName == variableName)
+ return false; // no need to introduce the variable here
+ }
+
+ UsingStatement usingStatement = node as UsingStatement;
+ if (usingStatement != null) {
+ VariableDeclarationStatement varDecl = usingStatement.ResourceAcquisition as VariableDeclarationStatement;
+ if (varDecl != null) {
+ foreach (VariableInitializer v in varDecl.Variables) {
+ if (v.Name == variableName)
+ return false; // no need to introduce the variable here
+ }
+ }
+ }
+
+ CatchClause catchClause = node as CatchClause;
+ if (catchClause != null && catchClause.VariableName == variableName) {
+ return false; // no need to introduce the variable here
+ }
+
+ for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
+ if (UsesVariable(child, variableName))
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
new file mode 100644
index 00000000..04b2293d
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
@@ -0,0 +1,502 @@
+// 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.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.ILAst;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.PatternMatching;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ /// <summary>
+ /// Converts "new Action(obj, ldftn(func))" into "new Action(obj.func)".
+ /// For anonymous methods, creates an AnonymousMethodExpression.
+ /// Also gets rid of any "Display Classes" left over after inlining an anonymous method.
+ /// </summary>
+ public class DelegateConstruction : ContextTrackingVisitor<object>
+ {
+ internal sealed class Annotation
+ {
+ /// <summary>
+ /// ldftn or ldvirtftn?
+ /// </summary>
+ public readonly bool IsVirtual;
+
+ public Annotation(bool isVirtual)
+ {
+ IsVirtual = isVirtual;
+ }
+ }
+
+ internal sealed class CapturedVariableAnnotation
+ {
+ }
+
+ List<string> currentlyUsedVariableNames = new List<string>();
+
+ public DelegateConstruction(DecompilerContext context) : base(context)
+ {
+ }
+
+ public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data)
+ {
+ if (objectCreateExpression.Arguments.Count == 2) {
+ Expression obj = objectCreateExpression.Arguments.First();
+ Expression func = objectCreateExpression.Arguments.Last();
+ Annotation annotation = func.Annotation<Annotation>();
+ if (annotation != null) {
+ IdentifierExpression methodIdent = (IdentifierExpression)((InvocationExpression)func).Arguments.Single();
+ MethodReference method = methodIdent.Annotation<MethodReference>();
+ if (method != null) {
+ if (HandleAnonymousMethod(objectCreateExpression, obj, method))
+ return null;
+ // Perform the transformation to "new Action(obj.func)".
+ obj.Remove();
+ methodIdent.Remove();
+ if (!annotation.IsVirtual && obj is ThisReferenceExpression) {
+ // maybe it's getting the pointer of a base method?
+ if (method.DeclaringType.GetElementType() != context.CurrentType) {
+ obj = new BaseReferenceExpression();
+ }
+ }
+ if (!annotation.IsVirtual && obj is NullReferenceExpression && !method.HasThis) {
+ // We're loading a static method.
+ // However it is possible to load extension methods with an instance, so we compare the number of arguments:
+ bool isExtensionMethod = false;
+ TypeReference delegateType = objectCreateExpression.Type.Annotation<TypeReference>();
+ if (delegateType != null) {
+ TypeDefinition delegateTypeDef = delegateType.Resolve();
+ if (delegateTypeDef != null) {
+ MethodDefinition invokeMethod = delegateTypeDef.Methods.FirstOrDefault(m => m.Name == "Invoke");
+ if (invokeMethod != null) {
+ isExtensionMethod = (invokeMethod.Parameters.Count + 1 == method.Parameters.Count);
+ }
+ }
+ }
+ if (!isExtensionMethod) {
+ obj = new TypeReferenceExpression { Type = AstBuilder.ConvertType(method.DeclaringType) };
+ }
+ }
+ // now transform the identifier into a member reference
+ MemberReferenceExpression mre = new MemberReferenceExpression();
+ mre.Target = obj;
+ mre.MemberName = methodIdent.Identifier;
+ methodIdent.TypeArguments.MoveTo(mre.TypeArguments);
+ mre.AddAnnotation(method);
+ objectCreateExpression.Arguments.Clear();
+ objectCreateExpression.Arguments.Add(mre);
+ return null;
+ }
+ }
+ }
+ return base.VisitObjectCreateExpression(objectCreateExpression, data);
+ }
+
+ internal static bool IsAnonymousMethod(DecompilerContext context, MethodDefinition method)
+ {
+ if (method == null || !(method.HasGeneratedName() || method.Name.Contains("$")))
+ return false;
+ if (!(method.IsCompilerGenerated() || IsPotentialClosure(context, method.DeclaringType)))
+ return false;
+ return true;
+ }
+
+ bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, MethodReference methodRef)
+ {
+ if (!context.Settings.AnonymousMethods)
+ return false; // anonymous method decompilation is disabled
+ if (target != null && !(target is IdentifierExpression || target is ThisReferenceExpression || target is NullReferenceExpression))
+ return false; // don't copy arbitrary expressions, deal with identifiers only
+
+ // Anonymous methods are defined in the same assembly
+ MethodDefinition method = methodRef.ResolveWithinSameModule();
+ if (!IsAnonymousMethod(context, method))
+ return false;
+
+ // Create AnonymousMethodExpression and prepare parameters
+ AnonymousMethodExpression ame = new AnonymousMethodExpression();
+ ame.CopyAnnotationsFrom(objectCreateExpression); // copy ILRanges etc.
+ ame.RemoveAnnotations<MethodReference>(); // remove reference to delegate ctor
+ ame.AddAnnotation(method); // add reference to anonymous method
+ ame.Parameters.AddRange(AstBuilder.MakeParameters(method, isLambda: true));
+ ame.HasParameterList = true;
+
+ // rename variables so that they don't conflict with the parameters:
+ foreach (ParameterDeclaration pd in ame.Parameters) {
+ EnsureVariableNameIsAvailable(objectCreateExpression, pd.Name);
+ }
+
+ // Decompile the anonymous method:
+
+ DecompilerContext subContext = context.Clone();
+ subContext.CurrentMethod = method;
+ subContext.CurrentMethodIsAsync = false;
+ subContext.ReservedVariableNames.AddRange(currentlyUsedVariableNames);
+ BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method, subContext, ame.Parameters);
+ TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction, subContext);
+ body.AcceptVisitor(this, null);
+
+
+ bool isLambda = false;
+ if (ame.Parameters.All(p => p.ParameterModifier == ParameterModifier.None)) {
+ isLambda = (body.Statements.Count == 1 && body.Statements.Single() is ReturnStatement);
+ }
+ // Remove the parameter list from an AnonymousMethodExpression if the original method had no names,
+ // and the parameters are not used in the method body
+ if (!isLambda && method.Parameters.All(p => string.IsNullOrEmpty(p.Name))) {
+ var parameterReferencingIdentifiers =
+ from ident in body.Descendants.OfType<IdentifierExpression>()
+ let v = ident.Annotation<ILVariable>()
+ where v != null && v.IsParameter && method.Parameters.Contains(v.OriginalParameter)
+ select ident;
+ if (!parameterReferencingIdentifiers.Any()) {
+ ame.Parameters.Clear();
+ ame.HasParameterList = false;
+ }
+ }
+
+ // Replace all occurrences of 'this' in the method body with the delegate's target:
+ foreach (AstNode node in body.Descendants) {
+ if (node is ThisReferenceExpression)
+ node.ReplaceWith(target.Clone());
+ }
+ Expression replacement;
+ if (isLambda) {
+ LambdaExpression lambda = new LambdaExpression();
+ lambda.CopyAnnotationsFrom(ame);
+ ame.Parameters.MoveTo(lambda.Parameters);
+ Expression returnExpr = ((ReturnStatement)body.Statements.Single()).Expression;
+ returnExpr.Remove();
+ lambda.Body = returnExpr;
+ replacement = lambda;
+ } else {
+ ame.Body = body;
+ replacement = ame;
+ }
+ var expectedType = objectCreateExpression.Annotation<TypeInformation>().ExpectedType.Resolve();
+ if (expectedType != null && !expectedType.IsDelegate()) {
+ var simplifiedDelegateCreation = (ObjectCreateExpression)objectCreateExpression.Clone();
+ simplifiedDelegateCreation.Arguments.Clear();
+ simplifiedDelegateCreation.Arguments.Add(replacement);
+ replacement = simplifiedDelegateCreation;
+ }
+ objectCreateExpression.ReplaceWith(replacement);
+ return true;
+ }
+
+ internal static bool IsPotentialClosure(DecompilerContext context, TypeDefinition potentialDisplayClass)
+ {
+ if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
+ return false;
+ // check that methodContainingType is within containingType
+ while (potentialDisplayClass != context.CurrentType) {
+ potentialDisplayClass = potentialDisplayClass.DeclaringType;
+ if (potentialDisplayClass == null)
+ return false;
+ }
+ return true;
+ }
+
+ public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data)
+ {
+ if (context.Settings.ExpressionTrees && ExpressionTreeConverter.CouldBeExpressionTree(invocationExpression)) {
+ Expression converted = ExpressionTreeConverter.TryConvert(context, invocationExpression);
+ if (converted != null) {
+ invocationExpression.ReplaceWith(converted);
+ return converted.AcceptVisitor(this, data);
+ }
+ }
+ return base.VisitInvocationExpression(invocationExpression, data);
+ }
+
+ #region Track current variables
+ public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
+ {
+ Debug.Assert(currentlyUsedVariableNames.Count == 0);
+ try {
+ currentlyUsedVariableNames.AddRange(methodDeclaration.Parameters.Select(p => p.Name));
+ return base.VisitMethodDeclaration(methodDeclaration, data);
+ } finally {
+ currentlyUsedVariableNames.Clear();
+ }
+ }
+
+ public override object VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data)
+ {
+ Debug.Assert(currentlyUsedVariableNames.Count == 0);
+ try {
+ currentlyUsedVariableNames.AddRange(operatorDeclaration.Parameters.Select(p => p.Name));
+ return base.VisitOperatorDeclaration(operatorDeclaration, data);
+ } finally {
+ currentlyUsedVariableNames.Clear();
+ }
+ }
+
+ public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
+ {
+ Debug.Assert(currentlyUsedVariableNames.Count == 0);
+ try {
+ currentlyUsedVariableNames.AddRange(constructorDeclaration.Parameters.Select(p => p.Name));
+ return base.VisitConstructorDeclaration(constructorDeclaration, data);
+ } finally {
+ currentlyUsedVariableNames.Clear();
+ }
+ }
+
+ public override object VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration, object data)
+ {
+ Debug.Assert(currentlyUsedVariableNames.Count == 0);
+ try {
+ currentlyUsedVariableNames.AddRange(indexerDeclaration.Parameters.Select(p => p.Name));
+ return base.VisitIndexerDeclaration(indexerDeclaration, data);
+ } finally {
+ currentlyUsedVariableNames.Clear();
+ }
+ }
+
+ public override object VisitAccessor(Accessor accessor, object data)
+ {
+ try {
+ currentlyUsedVariableNames.Add("value");
+ return base.VisitAccessor(accessor, data);
+ } finally {
+ currentlyUsedVariableNames.RemoveAt(currentlyUsedVariableNames.Count - 1);
+ }
+ }
+
+ public override object VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, object data)
+ {
+ foreach (VariableInitializer v in variableDeclarationStatement.Variables)
+ currentlyUsedVariableNames.Add(v.Name);
+ return base.VisitVariableDeclarationStatement(variableDeclarationStatement, data);
+ }
+
+ public override object VisitFixedStatement(FixedStatement fixedStatement, object data)
+ {
+ foreach (VariableInitializer v in fixedStatement.Variables)
+ currentlyUsedVariableNames.Add(v.Name);
+ return base.VisitFixedStatement(fixedStatement, data);
+ }
+ #endregion
+
+ static readonly ExpressionStatement displayClassAssignmentPattern =
+ new ExpressionStatement(new AssignmentExpression(
+ new NamedNode("variable", new IdentifierExpression(Pattern.AnyString)),
+ new ObjectCreateExpression { Type = new AnyNode("type") }
+ ));
+
+ public override object VisitBlockStatement(BlockStatement blockStatement, object data)
+ {
+ int numberOfVariablesOutsideBlock = currentlyUsedVariableNames.Count;
+ base.VisitBlockStatement(blockStatement, data);
+ foreach (ExpressionStatement stmt in blockStatement.Statements.OfType<ExpressionStatement>().ToArray()) {
+ Match displayClassAssignmentMatch = displayClassAssignmentPattern.Match(stmt);
+ if (!displayClassAssignmentMatch.Success)
+ continue;
+
+ ILVariable variable = displayClassAssignmentMatch.Get<AstNode>("variable").Single().Annotation<ILVariable>();
+ if (variable == null)
+ continue;
+ TypeDefinition type = variable.Type.ResolveWithinSameModule();
+ if (!IsPotentialClosure(context, type))
+ continue;
+ if (displayClassAssignmentMatch.Get<AstType>("type").Single().Annotation<TypeReference>().ResolveWithinSameModule() != type)
+ continue;
+
+ // Looks like we found a display class creation. Now let's verify that the variable is used only for field accesses:
+ bool ok = true;
+ foreach (var identExpr in blockStatement.Descendants.OfType<IdentifierExpression>()) {
+ if (identExpr.Identifier == variable.Name && identExpr != displayClassAssignmentMatch.Get("variable").Single()) {
+ if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.Annotation<FieldReference>() != null))
+ ok = false;
+ }
+ }
+ if (!ok)
+ continue;
+ Dictionary<FieldReference, AstNode> dict = new Dictionary<FieldReference, AstNode>();
+
+ // Delete the variable declaration statement:
+ VariableDeclarationStatement displayClassVarDecl = PatternStatementTransform.FindVariableDeclaration(stmt, variable.Name);
+ if (displayClassVarDecl != null)
+ displayClassVarDecl.Remove();
+
+ // Delete the assignment statement:
+ AstNode cur = stmt.NextSibling;
+ stmt.Remove();
+
+ // Delete any following statements as long as they assign parameters to the display class
+ BlockStatement rootBlock = blockStatement.Ancestors.OfType<BlockStatement>().LastOrDefault() ?? blockStatement;
+ List<ILVariable> parameterOccurrances = rootBlock.Descendants.OfType<IdentifierExpression>()
+ .Select(n => n.Annotation<ILVariable>()).Where(p => p != null && p.IsParameter).ToList();
+ AstNode next;
+ for (; cur != null; cur = next) {
+ next = cur.NextSibling;
+
+ // Test for the pattern:
+ // "variableName.MemberName = right;"
+ ExpressionStatement closureFieldAssignmentPattern = new ExpressionStatement(
+ new AssignmentExpression(
+ new NamedNode("left", new MemberReferenceExpression {
+ Target = new IdentifierExpression(variable.Name),
+ MemberName = Pattern.AnyString
+ }),
+ new AnyNode("right")
+ )
+ );
+ Match m = closureFieldAssignmentPattern.Match(cur);
+ if (m.Success) {
+ FieldDefinition fieldDef = m.Get<MemberReferenceExpression>("left").Single().Annotation<FieldReference>().ResolveWithinSameModule();
+ AstNode right = m.Get<AstNode>("right").Single();
+ bool isParameter = false;
+ bool isDisplayClassParentPointerAssignment = false;
+ if (right is ThisReferenceExpression) {
+ isParameter = true;
+ } else if (right is IdentifierExpression) {
+ // handle parameters only if the whole method contains no other occurrence except for 'right'
+ ILVariable v = right.Annotation<ILVariable>();
+ isParameter = v.IsParameter && parameterOccurrances.Count(c => c == v) == 1;
+ if (!isParameter && IsPotentialClosure(context, v.Type.ResolveWithinSameModule())) {
+ // parent display class within the same method
+ // (closure2.localsX = closure1;)
+ isDisplayClassParentPointerAssignment = true;
+ }
+ } else if (right is MemberReferenceExpression) {
+ // copy of parent display class reference from an outer lambda
+ // closure2.localsX = this.localsY
+ MemberReferenceExpression mre = m.Get<MemberReferenceExpression>("right").Single();
+ do {
+ // descend into the targets of the mre as long as the field types are closures
+ FieldDefinition fieldDef2 = mre.Annotation<FieldReference>().ResolveWithinSameModule();
+ if (fieldDef2 == null || !IsPotentialClosure(context, fieldDef2.FieldType.ResolveWithinSameModule())) {
+ break;
+ }
+ // if we finally get to a this reference, it's copying a display class parent pointer
+ if (mre.Target is ThisReferenceExpression) {
+ isDisplayClassParentPointerAssignment = true;
+ }
+ mre = mre.Target as MemberReferenceExpression;
+ } while (mre != null);
+ }
+ if (isParameter || isDisplayClassParentPointerAssignment) {
+ dict[fieldDef] = right;
+ cur.Remove();
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ // Now create variables for all fields of the display class (except for those that we already handled as parameters)
+ List<Tuple<AstType, ILVariable>> variablesToDeclare = new List<Tuple<AstType, ILVariable>>();
+ foreach (FieldDefinition field in type.Fields) {
+ if (field.IsStatic)
+ continue; // skip static fields
+ if (dict.ContainsKey(field)) // skip field if it already was handled as parameter
+ continue;
+ string capturedVariableName = field.Name;
+ if (capturedVariableName.StartsWith("$VB$Local_", StringComparison.Ordinal) && capturedVariableName.Length > 10)
+ capturedVariableName = capturedVariableName.Substring(10);
+ EnsureVariableNameIsAvailable(blockStatement, capturedVariableName);
+ currentlyUsedVariableNames.Add(capturedVariableName);
+ ILVariable ilVar = new ILVariable
+ {
+ IsGenerated = true,
+ Name = capturedVariableName,
+ Type = field.FieldType,
+ };
+ variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), ilVar));
+ dict[field] = new IdentifierExpression(capturedVariableName).WithAnnotation(ilVar);
+ }
+
+ // Now figure out where the closure was accessed and use the simpler replacement expression there:
+ foreach (var identExpr in blockStatement.Descendants.OfType<IdentifierExpression>()) {
+ if (identExpr.Identifier == variable.Name) {
+ MemberReferenceExpression mre = (MemberReferenceExpression)identExpr.Parent;
+ AstNode replacement;
+ if (dict.TryGetValue(mre.Annotation<FieldReference>().ResolveWithinSameModule(), out replacement)) {
+ mre.ReplaceWith(replacement.Clone());
+ }
+ }
+ }
+ // Now insert the variable declarations (we can do this after the replacements only so that the scope detection works):
+ Statement insertionPoint = blockStatement.Statements.FirstOrDefault();
+ foreach (var tuple in variablesToDeclare) {
+ var newVarDecl = new VariableDeclarationStatement(tuple.Item1, tuple.Item2.Name);
+ newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation());
+ newVarDecl.Variables.Single().AddAnnotation(tuple.Item2);
+ blockStatement.Statements.InsertBefore(insertionPoint, newVarDecl);
+ }
+ }
+ currentlyUsedVariableNames.RemoveRange(numberOfVariablesOutsideBlock, currentlyUsedVariableNames.Count - numberOfVariablesOutsideBlock);
+ return null;
+ }
+
+ void EnsureVariableNameIsAvailable(AstNode currentNode, string name)
+ {
+ int pos = currentlyUsedVariableNames.IndexOf(name);
+ if (pos < 0) {
+ // name is still available
+ return;
+ }
+ // Naming conflict. Let's rename the existing variable so that the field keeps the name from metadata.
+ NameVariables nv = new NameVariables();
+ // Add currently used variable and parameter names
+ foreach (string nameInUse in currentlyUsedVariableNames)
+ nv.AddExistingName(nameInUse);
+ // variables declared in child nodes of this block
+ foreach (VariableInitializer vi in currentNode.Descendants.OfType<VariableInitializer>())
+ nv.AddExistingName(vi.Name);
+ // parameters in child lambdas
+ foreach (ParameterDeclaration pd in currentNode.Descendants.OfType<ParameterDeclaration>())
+ nv.AddExistingName(pd.Name);
+
+ string newName = nv.GetAlternativeName(name);
+ currentlyUsedVariableNames[pos] = newName;
+
+ // find top-most block
+ AstNode topMostBlock = currentNode.Ancestors.OfType<BlockStatement>().LastOrDefault() ?? currentNode;
+
+ // rename identifiers
+ foreach (IdentifierExpression ident in topMostBlock.Descendants.OfType<IdentifierExpression>()) {
+ if (ident.Identifier == name) {
+ ident.Identifier = newName;
+ ILVariable v = ident.Annotation<ILVariable>();
+ if (v != null)
+ v.Name = newName;
+ }
+ }
+ // rename variable declarations
+ foreach (VariableInitializer vi in topMostBlock.Descendants.OfType<VariableInitializer>()) {
+ if (vi.Name == name) {
+ vi.Name = newName;
+ ILVariable v = vi.Annotation<ILVariable>();
+ if (v != null)
+ v.Name = newName;
+ }
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs b/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs
new file mode 100644
index 00000000..2327200d
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs
@@ -0,0 +1,875 @@
+// 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.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using ICSharpCode.Decompiler.ILAst;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.PatternMatching;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ public class ExpressionTreeConverter
+ {
+ #region static TryConvert method
+ public static bool CouldBeExpressionTree(InvocationExpression expr)
+ {
+ if (expr != null && expr.Arguments.Count == 2) {
+ MethodReference mr = expr.Annotation<MethodReference>();
+ return mr != null && mr.Name == "Lambda" && mr.DeclaringType.FullName == "System.Linq.Expressions.Expression";
+ }
+ return false;
+ }
+
+ public static Expression TryConvert(DecompilerContext context, Expression expr)
+ {
+ Expression converted = new ExpressionTreeConverter(context).Convert(expr);
+ if (converted != null) {
+ converted.AddAnnotation(new ExpressionTreeLambdaAnnotation());
+ }
+ return converted;
+ }
+ #endregion
+
+ readonly DecompilerContext context;
+ Stack<LambdaExpression> activeLambdas = new Stack<LambdaExpression>();
+
+ ExpressionTreeConverter(DecompilerContext context)
+ {
+ this.context = context;
+ }
+
+ #region Main Convert method
+ Expression Convert(Expression expr)
+ {
+ InvocationExpression invocation = expr as InvocationExpression;
+ if (invocation != null) {
+ MethodReference mr = invocation.Annotation<MethodReference>();
+ if (mr != null && mr.DeclaringType.FullName == "System.Linq.Expressions.Expression") {
+ switch (mr.Name) {
+ case "Add":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Add, false);
+ case "AddChecked":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Add, true);
+ case "AddAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Add, false);
+ case "AddAssignChecked":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Add, true);
+ case "And":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.BitwiseAnd);
+ case "AndAlso":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.ConditionalAnd);
+ case "AndAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.BitwiseAnd);
+ case "ArrayAccess":
+ case "ArrayIndex":
+ return ConvertArrayIndex(invocation);
+ case "ArrayLength":
+ return ConvertArrayLength(invocation);
+ case "Assign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Assign);
+ case "Call":
+ return ConvertCall(invocation);
+ case "Coalesce":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.NullCoalescing);
+ case "Condition":
+ return ConvertCondition(invocation);
+ case "Constant":
+ if (invocation.Arguments.Count >= 1)
+ return invocation.Arguments.First().Clone();
+ else
+ return NotSupported(expr);
+ case "Convert":
+ return ConvertCast(invocation, false);
+ case "ConvertChecked":
+ return ConvertCast(invocation, true);
+ case "Divide":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Divide);
+ case "DivideAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Divide);
+ case "Equal":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Equality);
+ case "ExclusiveOr":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.ExclusiveOr);
+ case "ExclusiveOrAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ExclusiveOr);
+ case "Field":
+ return ConvertField(invocation);
+ case "GreaterThan":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.GreaterThan);
+ case "GreaterThanOrEqual":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.GreaterThanOrEqual);
+ case "Invoke":
+ return ConvertInvoke(invocation);
+ case "Lambda":
+ return ConvertLambda(invocation);
+ case "LeftShift":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.ShiftLeft);
+ case "LeftShiftAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ShiftLeft);
+ case "LessThan":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.LessThan);
+ case "LessThanOrEqual":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.LessThanOrEqual);
+ case "ListInit":
+ return ConvertListInit(invocation);
+ case "MemberInit":
+ return ConvertMemberInit(invocation);
+ case "Modulo":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Modulus);
+ case "ModuloAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Modulus);
+ case "Multiply":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Multiply, false);
+ case "MultiplyChecked":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Multiply, true);
+ case "MultiplyAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Multiply, false);
+ case "MultiplyAssignChecked":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Multiply, true);
+ case "Negate":
+ return ConvertUnaryOperator(invocation, UnaryOperatorType.Minus, false);
+ case "NegateChecked":
+ return ConvertUnaryOperator(invocation, UnaryOperatorType.Minus, true);
+ case "New":
+ return ConvertNewObject(invocation);
+ case "NewArrayBounds":
+ return ConvertNewArrayBounds(invocation);
+ case "NewArrayInit":
+ return ConvertNewArrayInit(invocation);
+ case "Not":
+ return ConvertUnaryOperator(invocation, UnaryOperatorType.Not);
+ case "NotEqual":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.InEquality);
+ case "OnesComplement":
+ return ConvertUnaryOperator(invocation, UnaryOperatorType.BitNot);
+ case "Or":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.BitwiseOr);
+ case "OrAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.BitwiseOr);
+ case "OrElse":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.ConditionalOr);
+ case "Property":
+ return ConvertProperty(invocation);
+ case "Quote":
+ if (invocation.Arguments.Count == 1)
+ return Convert(invocation.Arguments.Single());
+ else
+ return NotSupported(invocation);
+ case "RightShift":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.ShiftRight);
+ case "RightShiftAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ShiftRight);
+ case "Subtract":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Subtract, false);
+ case "SubtractChecked":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Subtract, true);
+ case "SubtractAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Subtract, false);
+ case "SubtractAssignChecked":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Subtract, true);
+ case "TypeAs":
+ return ConvertTypeAs(invocation);
+ case "TypeIs":
+ return ConvertTypeIs(invocation);
+ }
+ }
+ }
+ IdentifierExpression ident = expr as IdentifierExpression;
+ if (ident != null) {
+ ILVariable v = ident.Annotation<ILVariable>();
+ if (v != null) {
+ foreach (LambdaExpression lambda in activeLambdas) {
+ foreach (ParameterDeclaration p in lambda.Parameters) {
+ if (p.Annotation<ILVariable>() == v)
+ return new IdentifierExpression(p.Name).WithAnnotation(v);
+ }
+ }
+ }
+ }
+ return NotSupported(expr);
+ }
+
+ Expression NotSupported(Expression expr)
+ {
+ Debug.WriteLine("Expression Tree Conversion Failed: '" + expr + "' is not supported");
+ return null;
+ }
+ #endregion
+
+ #region Convert Lambda
+ static readonly Expression emptyArrayPattern = new ArrayCreateExpression {
+ Type = new AnyNode(),
+ Arguments = { new PrimitiveExpression(0) }
+ };
+
+ Expression ConvertLambda(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return NotSupported(invocation);
+ LambdaExpression lambda = new LambdaExpression();
+ Expression body = invocation.Arguments.First();
+ ArrayCreateExpression parameterArray = invocation.Arguments.Last() as ArrayCreateExpression;
+ if (parameterArray == null)
+ return NotSupported(invocation);
+
+ var annotation = body.Annotation<ParameterDeclarationAnnotation>();
+ if (annotation != null) {
+ lambda.Parameters.AddRange(annotation.Parameters);
+ } else {
+ // No parameter declaration annotation found.
+ if (!emptyArrayPattern.IsMatch(parameterArray))
+ return null;
+ }
+
+ activeLambdas.Push(lambda);
+ Expression convertedBody = Convert(body);
+ activeLambdas.Pop();
+ if (convertedBody == null)
+ return null;
+ lambda.Body = convertedBody;
+ return lambda;
+ }
+ #endregion
+
+ #region Convert Field
+ static readonly Expression getFieldFromHandlePattern =
+ new TypePattern(typeof(FieldInfo)).ToType().Invoke(
+ "GetFieldFromHandle",
+ new LdTokenPattern("field").ToExpression().Member("FieldHandle"),
+ new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle"))
+ );
+
+ Expression ConvertField(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return NotSupported(invocation);
+
+ Expression fieldInfoExpr = invocation.Arguments.ElementAt(1);
+ Match m = getFieldFromHandlePattern.Match(fieldInfoExpr);
+ if (!m.Success)
+ return NotSupported(invocation);
+
+ FieldReference fr = m.Get<AstNode>("field").Single().Annotation<FieldReference>();
+ if (fr == null)
+ return null;
+
+ Expression target = invocation.Arguments.ElementAt(0);
+ Expression convertedTarget;
+ if (target is NullReferenceExpression) {
+ if (m.Has("declaringType"))
+ convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone());
+ else
+ convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(fr.DeclaringType));
+ } else {
+ convertedTarget = Convert(target);
+ if (convertedTarget == null)
+ return null;
+ }
+
+ return convertedTarget.Member(fr.Name).WithAnnotation(fr);
+ }
+ #endregion
+
+ #region Convert Property
+ static readonly Expression getMethodFromHandlePattern =
+ new TypePattern(typeof(MethodBase)).ToType().Invoke(
+ "GetMethodFromHandle",
+ new LdTokenPattern("method").ToExpression().Member("MethodHandle"),
+ new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle"))
+ ).CastTo(new TypePattern(typeof(MethodInfo)));
+
+ Expression ConvertProperty(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return NotSupported(invocation);
+
+ Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1));
+ if (!m.Success)
+ return NotSupported(invocation);
+
+ MethodReference mr = m.Get<AstNode>("method").Single().Annotation<MethodReference>();
+ if (mr == null)
+ return null;
+
+ Expression target = invocation.Arguments.ElementAt(0);
+ Expression convertedTarget;
+ if (target is NullReferenceExpression) {
+ if (m.Has("declaringType"))
+ convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone());
+ else
+ convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(mr.DeclaringType));
+ } else {
+ convertedTarget = Convert(target);
+ if (convertedTarget == null)
+ return null;
+ }
+
+ return convertedTarget.Member(GetPropertyName(mr)).WithAnnotation(mr);
+ }
+
+ string GetPropertyName(MethodReference accessor)
+ {
+ string name = accessor.Name;
+ if (name.StartsWith("get_", StringComparison.Ordinal) || name.StartsWith("set_", StringComparison.Ordinal))
+ name = name.Substring(4);
+ return name;
+ }
+ #endregion
+
+ #region Convert Call
+ Expression ConvertCall(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count < 2)
+ return NotSupported(invocation);
+
+ Expression target;
+ int firstArgumentPosition;
+
+ Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(0));
+ if (m.Success) {
+ target = null;
+ firstArgumentPosition = 1;
+ } else {
+ m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1));
+ if (!m.Success)
+ return NotSupported(invocation);
+ target = invocation.Arguments.ElementAt(0);
+ firstArgumentPosition = 2;
+ }
+
+ MethodReference mr = m.Get<AstNode>("method").Single().Annotation<MethodReference>();
+ if (mr == null)
+ return null;
+
+ Expression convertedTarget;
+ if (target == null || target is NullReferenceExpression) {
+ // static method
+ if (m.Has("declaringType"))
+ convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone());
+ else
+ convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(mr.DeclaringType));
+ } else {
+ convertedTarget = Convert(target);
+ if (convertedTarget == null)
+ return null;
+ }
+
+ MemberReferenceExpression mre = convertedTarget.Member(mr.Name);
+ GenericInstanceMethod gim = mr as GenericInstanceMethod;
+ if (gim != null) {
+ foreach (TypeReference tr in gim.GenericArguments) {
+ mre.TypeArguments.Add(AstBuilder.ConvertType(tr));
+ }
+ }
+ IList<Expression> arguments = null;
+ if (invocation.Arguments.Count == firstArgumentPosition + 1) {
+ Expression argumentArray = invocation.Arguments.ElementAt(firstArgumentPosition);
+ arguments = ConvertExpressionsArray(argumentArray);
+ }
+ if (arguments == null) {
+ arguments = new List<Expression>();
+ foreach (Expression argument in invocation.Arguments.Skip(firstArgumentPosition)) {
+ Expression convertedArgument = Convert(argument);
+ if (convertedArgument == null)
+ return null;
+ arguments.Add(convertedArgument);
+ }
+ }
+ MethodDefinition methodDef = mr.Resolve();
+ if (methodDef != null && methodDef.IsGetter) {
+ PropertyDefinition indexer = AstMethodBodyBuilder.GetIndexer(methodDef);
+ if (indexer != null)
+ return new IndexerExpression(mre.Target.Detach(), arguments).WithAnnotation(indexer);
+ }
+ return new InvocationExpression(mre, arguments).WithAnnotation(mr);
+ }
+
+ Expression ConvertInvoke(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return NotSupported(invocation);
+
+ Expression convertedTarget = Convert(invocation.Arguments.ElementAt(0));
+ IList<Expression> convertedArguments = ConvertExpressionsArray(invocation.Arguments.ElementAt(1));
+ if (convertedTarget != null && convertedArguments != null)
+ return new InvocationExpression(convertedTarget, convertedArguments);
+ else
+ return null;
+ }
+ #endregion
+
+ #region Convert Binary Operator
+ static readonly Pattern trueOrFalse = new Choice {
+ new PrimitiveExpression(true),
+ new PrimitiveExpression(false)
+ };
+
+ Expression ConvertBinaryOperator(InvocationExpression invocation, BinaryOperatorType op, bool? isChecked = null)
+ {
+ if (invocation.Arguments.Count < 2)
+ return NotSupported(invocation);
+
+ Expression left = Convert(invocation.Arguments.ElementAt(0));
+ if (left == null)
+ return null;
+ Expression right = Convert(invocation.Arguments.ElementAt(1));
+ if (right == null)
+ return null;
+
+ BinaryOperatorExpression boe = new BinaryOperatorExpression(left, op, right);
+ if (isChecked != null)
+ boe.AddAnnotation(isChecked.Value ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
+
+ switch (invocation.Arguments.Count) {
+ case 2:
+ return boe;
+ case 3:
+ Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(2));
+ if (m.Success)
+ return boe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>());
+ else
+ return null;
+ case 4:
+ if (!trueOrFalse.IsMatch(invocation.Arguments.ElementAt(2)))
+ return null;
+ m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(3));
+ if (m.Success)
+ return boe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>());
+ else
+ return null;
+ default:
+ return NotSupported(invocation);
+ }
+ }
+ #endregion
+
+ #region Convert Assignment Operator
+ Expression ConvertAssignmentOperator(InvocationExpression invocation, AssignmentOperatorType op, bool? isChecked = null)
+ {
+ return NotSupported(invocation);
+ }
+ #endregion
+
+ #region Convert Unary Operator
+ Expression ConvertUnaryOperator(InvocationExpression invocation, UnaryOperatorType op, bool? isChecked = null)
+ {
+ if (invocation.Arguments.Count < 1)
+ return NotSupported(invocation);
+
+ Expression expr = Convert(invocation.Arguments.ElementAt(0));
+ if (expr == null)
+ return null;
+
+ UnaryOperatorExpression uoe = new UnaryOperatorExpression(op, expr);
+ if (isChecked != null)
+ uoe.AddAnnotation(isChecked.Value ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
+
+ switch (invocation.Arguments.Count) {
+ case 1:
+ return uoe;
+ case 2:
+ Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1));
+ if (m.Success)
+ return uoe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>());
+ else
+ return null;
+ default:
+ return NotSupported(invocation);
+ }
+ }
+ #endregion
+
+ #region Convert Condition Operator
+ Expression ConvertCondition(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 3)
+ return NotSupported(invocation);
+
+ Expression condition = Convert(invocation.Arguments.ElementAt(0));
+ Expression trueExpr = Convert(invocation.Arguments.ElementAt(1));
+ Expression falseExpr = Convert(invocation.Arguments.ElementAt(2));
+ if (condition != null && trueExpr != null && falseExpr != null)
+ return new ConditionalExpression(condition, trueExpr, falseExpr);
+ else
+ return null;
+ }
+ #endregion
+
+ #region Convert New Object
+ static readonly Expression newObjectCtorPattern = new TypePattern(typeof(MethodBase)).ToType().Invoke
+ (
+ "GetMethodFromHandle",
+ new LdTokenPattern("ctor").ToExpression().Member("MethodHandle"),
+ new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle"))
+ ).CastTo(new TypePattern(typeof(ConstructorInfo)));
+
+ Expression ConvertNewObject(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count < 1 || invocation.Arguments.Count > 3)
+ return NotSupported(invocation);
+
+ Match m = newObjectCtorPattern.Match(invocation.Arguments.First());
+ if (!m.Success)
+ return NotSupported(invocation);
+
+ MethodReference ctor = m.Get<AstNode>("ctor").Single().Annotation<MethodReference>();
+ if (ctor == null)
+ return null;
+
+ AstType declaringTypeNode;
+ TypeReference declaringType;
+ if (m.Has("declaringType")) {
+ declaringTypeNode = m.Get<AstType>("declaringType").Single().Clone();
+ declaringType = declaringTypeNode.Annotation<TypeReference>();
+ } else {
+ declaringTypeNode = AstBuilder.ConvertType(ctor.DeclaringType);
+ declaringType = ctor.DeclaringType;
+ }
+ if (declaringTypeNode == null)
+ return null;
+
+ ObjectCreateExpression oce = new ObjectCreateExpression(declaringTypeNode);
+ if (invocation.Arguments.Count >= 2) {
+ IList<Expression> arguments = ConvertExpressionsArray(invocation.Arguments.ElementAtOrDefault(1));
+ if (arguments == null)
+ return null;
+ oce.Arguments.AddRange(arguments);
+ }
+ if (invocation.Arguments.Count >= 3 && declaringType.IsAnonymousType()) {
+ MethodDefinition resolvedCtor = ctor.Resolve();
+ if (resolvedCtor == null || resolvedCtor.Parameters.Count != oce.Arguments.Count)
+ return null;
+ AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression();
+ var arguments = oce.Arguments.ToArray();
+ if (AstMethodBodyBuilder.CanInferAnonymousTypePropertyNamesFromArguments(arguments, resolvedCtor.Parameters)) {
+ oce.Arguments.MoveTo(atce.Initializers);
+ } else {
+ for (int i = 0; i < resolvedCtor.Parameters.Count; i++) {
+ atce.Initializers.Add(
+ new NamedExpression {
+ Name = resolvedCtor.Parameters[i].Name,
+ Expression = arguments[i].Detach()
+ });
+ }
+ }
+ return atce;
+ }
+
+ return oce;
+ }
+ #endregion
+
+ #region Convert ListInit
+ static readonly Pattern elementInitArrayPattern = ArrayInitializationPattern(
+ typeof(System.Linq.Expressions.ElementInit),
+ new TypePattern(typeof(System.Linq.Expressions.Expression)).ToType().Invoke("ElementInit", new AnyNode("methodInfos"), new AnyNode("addArgumentsArrays"))
+ );
+
+ Expression ConvertListInit(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return NotSupported(invocation);
+ ObjectCreateExpression oce = Convert(invocation.Arguments.ElementAt(0)) as ObjectCreateExpression;
+ if (oce == null)
+ return null;
+ Expression elementsArray = invocation.Arguments.ElementAt(1);
+ ArrayInitializerExpression initializer = ConvertElementInit(elementsArray);
+ if (initializer != null) {
+ oce.Initializer = initializer;
+ return oce;
+ } else {
+ return null;
+ }
+ }
+
+ ArrayInitializerExpression ConvertElementInit(Expression elementsArray)
+ {
+ IList<Expression> elements = ConvertExpressionsArray(elementsArray);
+ if (elements != null) {
+ return new ArrayInitializerExpression(elements);
+ }
+ Match m = elementInitArrayPattern.Match(elementsArray);
+ if (!m.Success)
+ return null;
+ ArrayInitializerExpression result = new ArrayInitializerExpression();
+ foreach (var elementInit in m.Get<Expression>("addArgumentsArrays")) {
+ IList<Expression> arguments = ConvertExpressionsArray(elementInit);
+ if (arguments == null)
+ return null;
+ result.Elements.Add(new ArrayInitializerExpression(arguments));
+ }
+ return result;
+ }
+ #endregion
+
+ #region Convert MemberInit
+ Expression ConvertMemberInit(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return NotSupported(invocation);
+ ObjectCreateExpression oce = Convert(invocation.Arguments.ElementAt(0)) as ObjectCreateExpression;
+ if (oce == null)
+ return null;
+ Expression elementsArray = invocation.Arguments.ElementAt(1);
+ ArrayInitializerExpression bindings = ConvertMemberBindings(elementsArray);
+ if (bindings == null)
+ return null;
+ oce.Initializer = bindings;
+ return oce;
+ }
+
+ static readonly Pattern memberBindingArrayPattern = ArrayInitializationPattern(typeof(System.Linq.Expressions.MemberBinding), new AnyNode("binding"));
+ static readonly INode expressionTypeReference = new TypeReferenceExpression(new TypePattern(typeof(System.Linq.Expressions.Expression)));
+
+ ArrayInitializerExpression ConvertMemberBindings(Expression elementsArray)
+ {
+ Match m = memberBindingArrayPattern.Match(elementsArray);
+ if (!m.Success)
+ return null;
+ ArrayInitializerExpression result = new ArrayInitializerExpression();
+ foreach (var binding in m.Get<Expression>("binding")) {
+ InvocationExpression bindingInvocation = binding as InvocationExpression;
+ if (bindingInvocation == null || bindingInvocation.Arguments.Count != 2)
+ return null;
+ MemberReferenceExpression bindingMRE = bindingInvocation.Target as MemberReferenceExpression;
+ if (bindingMRE == null || !expressionTypeReference.IsMatch(bindingMRE.Target))
+ return null;
+
+ Expression bindingTarget = bindingInvocation.Arguments.ElementAt(0);
+ Expression bindingValue = bindingInvocation.Arguments.ElementAt(1);
+
+ string memberName;
+ Match m2 = getMethodFromHandlePattern.Match(bindingTarget);
+ if (m2.Success) {
+ MethodReference setter = m2.Get<AstNode>("method").Single().Annotation<MethodReference>();
+ if (setter == null)
+ return null;
+ memberName = GetPropertyName(setter);
+ } else {
+ return null;
+ }
+
+ Expression convertedValue;
+ switch (bindingMRE.MemberName) {
+ case "Bind":
+ convertedValue = Convert(bindingValue);
+ break;
+ case "MemberBind":
+ convertedValue = ConvertMemberBindings(bindingValue);
+ break;
+ case "ListBind":
+ convertedValue = ConvertElementInit(bindingValue);
+ break;
+ default:
+ return null;
+ }
+ if (convertedValue == null)
+ return null;
+ result.Elements.Add(new NamedExpression(memberName, convertedValue));
+ }
+ return result;
+ }
+ #endregion
+
+ #region Convert Cast
+ Expression ConvertCast(InvocationExpression invocation, bool isChecked)
+ {
+ if (invocation.Arguments.Count < 2)
+ return null;
+ Expression converted = Convert(invocation.Arguments.ElementAt(0));
+ AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1));
+ if (converted != null && type != null) {
+ CastExpression cast = converted.CastTo(type);
+ cast.AddAnnotation(isChecked ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
+ switch (invocation.Arguments.Count) {
+ case 2:
+ return cast;
+ case 3:
+ Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(2));
+ if (m.Success)
+ return cast.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>());
+ else
+ return null;
+ }
+ }
+ return null;
+ }
+ #endregion
+
+ #region ConvertExpressionsArray
+ static Pattern ArrayInitializationPattern(Type arrayElementType, INode elementPattern)
+ {
+ return new Choice {
+ new ArrayCreateExpression {
+ Type = new TypePattern(arrayElementType),
+ Arguments = { new PrimitiveExpression(0) }
+ },
+ new ArrayCreateExpression {
+ Type = new TypePattern(arrayElementType),
+ AdditionalArraySpecifiers = { new ArraySpecifier() },
+ Initializer = new ArrayInitializerExpression {
+ Elements = { new Repeat(elementPattern) }
+ }
+ }
+ };
+ }
+
+ static readonly Pattern expressionArrayPattern = ArrayInitializationPattern(typeof(System.Linq.Expressions.Expression), new AnyNode("elements"));
+
+ IList<Expression> ConvertExpressionsArray(Expression arrayExpression)
+ {
+ Match m = expressionArrayPattern.Match(arrayExpression);
+ if (m.Success) {
+ List<Expression> result = new List<Expression>();
+ foreach (Expression expr in m.Get<Expression>("elements")) {
+ Expression converted = Convert(expr);
+ if (converted == null)
+ return null;
+ result.Add(converted);
+ }
+ return result;
+ }
+ return null;
+ }
+ #endregion
+
+ #region Convert TypeAs/TypeIs
+ static readonly TypeOfPattern typeOfPattern = new TypeOfPattern("type");
+
+ AstType ConvertTypeReference(Expression typeOfExpression)
+ {
+ Match m = typeOfPattern.Match(typeOfExpression);
+ if (m.Success)
+ return m.Get<AstType>("type").Single().Clone();
+ else
+ return null;
+ }
+
+ Expression ConvertTypeAs(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return null;
+ Expression converted = Convert(invocation.Arguments.ElementAt(0));
+ AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1));
+ if (converted != null && type != null)
+ return new AsExpression(converted, type);
+ return null;
+ }
+
+ Expression ConvertTypeIs(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return null;
+ Expression converted = Convert(invocation.Arguments.ElementAt(0));
+ AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1));
+ if (converted != null && type != null)
+ return new IsExpression { Expression = converted, Type = type };
+ return null;
+ }
+ #endregion
+
+ #region Convert Array
+ Expression ConvertArrayIndex(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return NotSupported(invocation);
+
+ Expression targetConverted = Convert(invocation.Arguments.First());
+ if (targetConverted == null)
+ return null;
+
+ Expression index = invocation.Arguments.ElementAt(1);
+ Expression indexConverted = Convert(index);
+ if (indexConverted != null) {
+ return new IndexerExpression(targetConverted, indexConverted);
+ }
+ IList<Expression> indexesConverted = ConvertExpressionsArray(index);
+ if (indexesConverted != null) {
+ return new IndexerExpression(targetConverted, indexesConverted);
+ }
+ return null;
+ }
+
+ Expression ConvertArrayLength(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 1)
+ return NotSupported(invocation);
+
+ Expression targetConverted = Convert(invocation.Arguments.Single());
+ if (targetConverted != null)
+ return targetConverted.Member("Length");
+ else
+ return null;
+ }
+
+ Expression ConvertNewArrayInit(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return NotSupported(invocation);
+
+ AstType elementType = ConvertTypeReference(invocation.Arguments.ElementAt(0));
+ IList<Expression> elements = ConvertExpressionsArray(invocation.Arguments.ElementAt(1));
+ if (elementType != null && elements != null) {
+ if (ContainsAnonymousType(elementType)) {
+ elementType = null;
+ }
+ return new ArrayCreateExpression {
+ Type = elementType,
+ AdditionalArraySpecifiers = { new ArraySpecifier() },
+ Initializer = new ArrayInitializerExpression(elements)
+ };
+ }
+ return null;
+ }
+
+ Expression ConvertNewArrayBounds(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return NotSupported(invocation);
+
+ AstType elementType = ConvertTypeReference(invocation.Arguments.ElementAt(0));
+ IList<Expression> arguments = ConvertExpressionsArray(invocation.Arguments.ElementAt(1));
+ if (elementType != null && arguments != null) {
+ if (ContainsAnonymousType(elementType)) {
+ elementType = null;
+ }
+ ArrayCreateExpression ace = new ArrayCreateExpression();
+ ace.Type = elementType;
+ ace.Arguments.AddRange(arguments);
+ return ace;
+ }
+ return null;
+ }
+
+ bool ContainsAnonymousType(AstType type)
+ {
+ foreach (AstType t in type.DescendantsAndSelf.OfType<AstType>()) {
+ TypeReference tr = t.Annotation<TypeReference>();
+ if (tr != null && tr.IsAnonymousType())
+ return true;
+ }
+ return false;
+ }
+ #endregion
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/FlattenSwitchBlocks.cs b/ICSharpCode.Decompiler/Ast/Transforms/FlattenSwitchBlocks.cs
new file mode 100644
index 00000000..9595e81b
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/FlattenSwitchBlocks.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using ICSharpCode.NRefactory.CSharp;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ internal class FlattenSwitchBlocks : IAstTransform
+ {
+ public void Run(AstNode compilationUnit)
+ {
+ foreach (var switchSection in compilationUnit.Descendants.OfType<SwitchSection>())
+ {
+ if (switchSection.Statements.Count != 1)
+ continue;
+
+ var blockStatement = switchSection.Statements.First() as BlockStatement;
+ if (blockStatement == null || blockStatement.Statements.Any(st => st is VariableDeclarationStatement))
+ continue;
+
+ blockStatement.Remove();
+ blockStatement.Statements.MoveTo(switchSection.Statements);
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/IntroduceExtensionMethods.cs b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceExtensionMethods.cs
new file mode 100644
index 00000000..9f05285e
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceExtensionMethods.cs
@@ -0,0 +1,66 @@
+// 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.Linq;
+using ICSharpCode.NRefactory.CSharp;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ /// <summary>
+ /// Converts extension method calls into infix syntax.
+ /// </summary>
+ public class IntroduceExtensionMethods : IAstTransform
+ {
+ readonly DecompilerContext context;
+
+ public IntroduceExtensionMethods(DecompilerContext context)
+ {
+ this.context = context;
+ }
+
+ public void Run(AstNode compilationUnit)
+ {
+ foreach (InvocationExpression invocation in compilationUnit.Descendants.OfType<InvocationExpression>()) {
+ MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression;
+ MethodReference methodReference = invocation.Annotation<MethodReference>();
+ if (mre != null && mre.Target is TypeReferenceExpression && methodReference != null && invocation.Arguments.Any()) {
+ MethodDefinition d = methodReference.Resolve();
+ if (d != null) {
+ foreach (var ca in d.CustomAttributes) {
+ if (ca.AttributeType.Name == "ExtensionAttribute" && ca.AttributeType.Namespace == "System.Runtime.CompilerServices") {
+ var firstArgument = invocation.Arguments.First();
+ if (firstArgument is NullReferenceExpression)
+ firstArgument = firstArgument.ReplaceWith(expr => expr.CastTo(AstBuilder.ConvertType(d.Parameters.First().ParameterType)));
+ else
+ mre.Target = firstArgument.Detach();
+ if (invocation.Arguments.Any()) {
+ // HACK: removing type arguments should be done indepently from whether a method is an extension method,
+ // just by testing whether the arguments can be inferred
+ mre.TypeArguments.Clear();
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/IntroduceQueryExpressions.cs b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceQueryExpressions.cs
new file mode 100644
index 00000000..0da56fe9
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceQueryExpressions.cs
@@ -0,0 +1,295 @@
+// 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.Diagnostics;
+using System.Linq;
+using ICSharpCode.NRefactory.CSharp;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ /// <summary>
+ /// Decompiles query expressions.
+ /// Based on C# 4.0 spec, §7.16.2 Query expression translation
+ /// </summary>
+ public class IntroduceQueryExpressions : IAstTransform
+ {
+ readonly DecompilerContext context;
+
+ public IntroduceQueryExpressions(DecompilerContext context)
+ {
+ this.context = context;
+ }
+
+ public void Run(AstNode compilationUnit)
+ {
+ if (!context.Settings.QueryExpressions)
+ return;
+ DecompileQueries(compilationUnit);
+ // After all queries were decompiled, detect degenerate queries (queries not property terminated with 'select' or 'group')
+ // and fix them, either by adding a degenerate select, or by combining them with another query.
+ foreach (QueryExpression query in compilationUnit.Descendants.OfType<QueryExpression>()) {
+ QueryFromClause fromClause = (QueryFromClause)query.Clauses.First();
+ if (IsDegenerateQuery(query)) {
+ // introduce select for degenerate query
+ query.Clauses.Add(new QuerySelectClause { Expression = new IdentifierExpression(fromClause.Identifier) });
+ }
+ // See if the data source of this query is a degenerate query,
+ // and combine the queries if possible.
+ QueryExpression innerQuery = fromClause.Expression as QueryExpression;
+ while (IsDegenerateQuery(innerQuery)) {
+ QueryFromClause innerFromClause = (QueryFromClause)innerQuery.Clauses.First();
+ if (fromClause.Identifier != innerFromClause.Identifier)
+ break;
+ // Replace the fromClause with all clauses from the inner query
+ fromClause.Remove();
+ QueryClause insertionPos = null;
+ foreach (var clause in innerQuery.Clauses) {
+ query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach());
+ }
+ fromClause = innerFromClause;
+ innerQuery = fromClause.Expression as QueryExpression;
+ }
+ }
+ }
+
+ bool IsDegenerateQuery(QueryExpression query)
+ {
+ if (query == null)
+ return false;
+ var lastClause = query.Clauses.LastOrDefault();
+ return !(lastClause is QuerySelectClause || lastClause is QueryGroupClause);
+ }
+
+ void DecompileQueries(AstNode node)
+ {
+ QueryExpression query = DecompileQuery(node as InvocationExpression);
+ if (query != null)
+ node.ReplaceWith(query);
+ for (AstNode child = (query ?? node).FirstChild; child != null; child = child.NextSibling) {
+ DecompileQueries(child);
+ }
+ }
+
+ QueryExpression DecompileQuery(InvocationExpression invocation)
+ {
+ if (invocation == null)
+ return null;
+ MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression;
+ if (mre == null)
+ return null;
+ switch (mre.MemberName) {
+ case "Select":
+ {
+ if (invocation.Arguments.Count != 1)
+ return null;
+ string parameterName;
+ Expression body;
+ if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) {
+ QueryExpression query = new QueryExpression();
+ query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() });
+ query.Clauses.Add(new QuerySelectClause { Expression = body.Detach() });
+ return query;
+ }
+ return null;
+ }
+ case "GroupBy":
+ {
+ if (invocation.Arguments.Count == 2) {
+ string parameterName1, parameterName2;
+ Expression keySelector, elementSelector;
+ if (MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameterName1, out keySelector)
+ && MatchSimpleLambda(invocation.Arguments.ElementAt(1), out parameterName2, out elementSelector)
+ && parameterName1 == parameterName2)
+ {
+ QueryExpression query = new QueryExpression();
+ query.Clauses.Add(new QueryFromClause { Identifier = parameterName1, Expression = mre.Target.Detach() });
+ query.Clauses.Add(new QueryGroupClause { Projection = elementSelector.Detach(), Key = keySelector.Detach() });
+ return query;
+ }
+ } else if (invocation.Arguments.Count == 1) {
+ string parameterName;
+ Expression keySelector;
+ if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out keySelector)) {
+ QueryExpression query = new QueryExpression();
+ query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() });
+ query.Clauses.Add(new QueryGroupClause { Projection = new IdentifierExpression(parameterName), Key = keySelector.Detach() });
+ return query;
+ }
+ }
+ return null;
+ }
+ case "SelectMany":
+ {
+ if (invocation.Arguments.Count != 2)
+ return null;
+ string parameterName;
+ Expression collectionSelector;
+ if (!MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameterName, out collectionSelector))
+ return null;
+ LambdaExpression lambda = invocation.Arguments.ElementAt(1) as LambdaExpression;
+ if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) {
+ ParameterDeclaration p1 = lambda.Parameters.ElementAt(0);
+ ParameterDeclaration p2 = lambda.Parameters.ElementAt(1);
+ if (p1.Name == parameterName) {
+ QueryExpression query = new QueryExpression();
+ query.Clauses.Add(new QueryFromClause { Identifier = p1.Name, Expression = mre.Target.Detach() });
+ query.Clauses.Add(new QueryFromClause { Identifier = p2.Name, Expression = collectionSelector.Detach() });
+ query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() });
+ return query;
+ }
+ }
+ return null;
+ }
+ case "Where":
+ {
+ if (invocation.Arguments.Count != 1)
+ return null;
+ string parameterName;
+ Expression body;
+ if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) {
+ QueryExpression query = new QueryExpression();
+ query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() });
+ query.Clauses.Add(new QueryWhereClause { Condition = body.Detach() });
+ return query;
+ }
+ return null;
+ }
+ case "OrderBy":
+ case "OrderByDescending":
+ case "ThenBy":
+ case "ThenByDescending":
+ {
+ if (invocation.Arguments.Count != 1)
+ return null;
+ string parameterName;
+ Expression orderExpression;
+ if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out orderExpression)) {
+ if (ValidateThenByChain(invocation, parameterName)) {
+ QueryOrderClause orderClause = new QueryOrderClause();
+ InvocationExpression tmp = invocation;
+ while (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending") {
+ // insert new ordering at beginning
+ orderClause.Orderings.InsertAfter(
+ null, new QueryOrdering {
+ Expression = orderExpression.Detach(),
+ Direction = (mre.MemberName == "ThenBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending)
+ });
+
+ tmp = (InvocationExpression)mre.Target;
+ mre = (MemberReferenceExpression)tmp.Target;
+ MatchSimpleLambda(tmp.Arguments.Single(), out parameterName, out orderExpression);
+ }
+ // insert new ordering at beginning
+ orderClause.Orderings.InsertAfter(
+ null, new QueryOrdering {
+ Expression = orderExpression.Detach(),
+ Direction = (mre.MemberName == "OrderBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending)
+ });
+
+ QueryExpression query = new QueryExpression();
+ query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() });
+ query.Clauses.Add(orderClause);
+ return query;
+ }
+ }
+ return null;
+ }
+ case "Join":
+ case "GroupJoin":
+ {
+ if (invocation.Arguments.Count != 4)
+ return null;
+ Expression source1 = mre.Target;
+ Expression source2 = invocation.Arguments.ElementAt(0);
+ string elementName1, elementName2;
+ Expression key1, key2;
+ if (!MatchSimpleLambda(invocation.Arguments.ElementAt(1), out elementName1, out key1))
+ return null;
+ if (!MatchSimpleLambda(invocation.Arguments.ElementAt(2), out elementName2, out key2))
+ return null;
+ LambdaExpression lambda = invocation.Arguments.ElementAt(3) as LambdaExpression;
+ if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) {
+ ParameterDeclaration p1 = lambda.Parameters.ElementAt(0);
+ ParameterDeclaration p2 = lambda.Parameters.ElementAt(1);
+ if (p1.Name == elementName1 && (p2.Name == elementName2 || mre.MemberName == "GroupJoin")) {
+ QueryExpression query = new QueryExpression();
+ query.Clauses.Add(new QueryFromClause { Identifier = elementName1, Expression = source1.Detach() });
+ QueryJoinClause joinClause = new QueryJoinClause();
+ joinClause.JoinIdentifier = elementName2; // join elementName2
+ joinClause.InExpression = source2.Detach(); // in source2
+ joinClause.OnExpression = key1.Detach(); // on key1
+ joinClause.EqualsExpression = key2.Detach(); // equals key2
+ if (mre.MemberName == "GroupJoin") {
+ joinClause.IntoIdentifier = p2.Name; // into p2.Name
+ }
+ query.Clauses.Add(joinClause);
+ query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() });
+ return query;
+ }
+ }
+ return null;
+ }
+ default:
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Ensure that all ThenBy's are correct, and that the list of ThenBy's is terminated by an 'OrderBy' invocation.
+ /// </summary>
+ bool ValidateThenByChain(InvocationExpression invocation, string expectedParameterName)
+ {
+ if (invocation == null || invocation.Arguments.Count != 1)
+ return false;
+ MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression;
+ if (mre == null)
+ return false;
+ string parameterName;
+ Expression body;
+ if (!MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body))
+ return false;
+ if (parameterName != expectedParameterName)
+ return false;
+
+ if (mre.MemberName == "OrderBy" || mre.MemberName == "OrderByDescending")
+ return true;
+ else if (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending")
+ return ValidateThenByChain(mre.Target as InvocationExpression, expectedParameterName);
+ else
+ return false;
+ }
+
+ /// <summary>Matches simple lambdas of the form "a => b"</summary>
+ bool MatchSimpleLambda(Expression expr, out string parameterName, out Expression body)
+ {
+ LambdaExpression lambda = expr as LambdaExpression;
+ if (lambda != null && lambda.Parameters.Count == 1 && lambda.Body is Expression) {
+ ParameterDeclaration p = lambda.Parameters.Single();
+ if (p.ParameterModifier == ParameterModifier.None) {
+ parameterName = p.Name;
+ body = (Expression)lambda.Body;
+ return true;
+ }
+ }
+ parameterName = null;
+ body = null;
+ return false;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs
new file mode 100644
index 00000000..43548e38
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs
@@ -0,0 +1,106 @@
+// 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 ICSharpCode.NRefactory.CSharp;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ public class IntroduceUnsafeModifier : DepthFirstAstVisitor<object, bool>, IAstTransform
+ {
+ public static readonly object PointerArithmeticAnnotation = new PointerArithmetic();
+
+ sealed class PointerArithmetic {}
+
+ public void Run(AstNode compilationUnit)
+ {
+ compilationUnit.AcceptVisitor(this, null);
+ }
+
+ protected override bool VisitChildren(AstNode node, object data)
+ {
+ bool result = false;
+ for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
+ result |= child.AcceptVisitor(this, data);
+ }
+ if (result && node is EntityDeclaration && !(node is Accessor)) {
+ ((EntityDeclaration)node).Modifiers |= Modifiers.Unsafe;
+ return false;
+ }
+ return result;
+ }
+
+ public override bool VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, object data)
+ {
+ base.VisitPointerReferenceExpression(pointerReferenceExpression, data);
+ return true;
+ }
+
+ public override bool VisitComposedType(ComposedType composedType, object data)
+ {
+ if (composedType.PointerRank > 0)
+ return true;
+ else
+ return base.VisitComposedType(composedType, data);
+ }
+
+ public override bool VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data)
+ {
+ bool result = base.VisitUnaryOperatorExpression(unaryOperatorExpression, data);
+ if (unaryOperatorExpression.Operator == UnaryOperatorType.Dereference) {
+ BinaryOperatorExpression bop = unaryOperatorExpression.Expression as BinaryOperatorExpression;
+ if (bop != null && bop.Operator == BinaryOperatorType.Add && bop.Annotation<PointerArithmetic>() != null) {
+ // transform "*(ptr + int)" to "ptr[int]"
+ IndexerExpression indexer = new IndexerExpression();
+ indexer.Target = bop.Left.Detach();
+ indexer.Arguments.Add(bop.Right.Detach());
+ indexer.CopyAnnotationsFrom(unaryOperatorExpression);
+ indexer.CopyAnnotationsFrom(bop);
+ unaryOperatorExpression.ReplaceWith(indexer);
+ }
+ return true;
+ } else if (unaryOperatorExpression.Operator == UnaryOperatorType.AddressOf) {
+ return true;
+ } else {
+ return result;
+ }
+ }
+
+ public override bool VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data)
+ {
+ bool result = base.VisitMemberReferenceExpression(memberReferenceExpression, data);
+ UnaryOperatorExpression uoe = memberReferenceExpression.Target as UnaryOperatorExpression;
+ if (uoe != null && uoe.Operator == UnaryOperatorType.Dereference) {
+ PointerReferenceExpression pre = new PointerReferenceExpression();
+ pre.Target = uoe.Expression.Detach();
+ pre.MemberName = memberReferenceExpression.MemberName;
+ memberReferenceExpression.TypeArguments.MoveTo(pre.TypeArguments);
+ pre.CopyAnnotationsFrom(uoe);
+ pre.CopyAnnotationsFrom(memberReferenceExpression);
+ memberReferenceExpression.ReplaceWith(pre);
+ }
+ return result;
+ }
+
+ public override bool VisitStackAllocExpression(StackAllocExpression stackAllocExpression, object data)
+ {
+ base.VisitStackAllocExpression(stackAllocExpression, data);
+ return true;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
new file mode 100644
index 00000000..6e9cc4f5
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
@@ -0,0 +1,359 @@
+// 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.Generic;
+using System.Linq;
+using ICSharpCode.NRefactory.CSharp;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ /// <summary>
+ /// Introduces using declarations.
+ /// </summary>
+ public class IntroduceUsingDeclarations : IAstTransform
+ {
+ DecompilerContext context;
+
+ public IntroduceUsingDeclarations(DecompilerContext context)
+ {
+ this.context = context;
+ }
+
+ public void Run(AstNode compilationUnit)
+ {
+ // First determine all the namespaces that need to be imported:
+ compilationUnit.AcceptVisitor(new FindRequiredImports(this), null);
+
+ importedNamespaces.Add("System"); // always import System, even when not necessary
+
+ if (context.Settings.UsingDeclarations) {
+ // Now add using declarations for those namespaces:
+ foreach (string ns in importedNamespaces.OrderByDescending(n => n)) {
+ // we go backwards (OrderByDescending) through the list of namespaces because we insert them backwards
+ // (always inserting at the start of the list)
+ 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] };
+ }
+ compilationUnit.InsertChildAfter(null, new UsingDeclaration { Import = nsType }, SyntaxTree.MemberRole);
+ }
+ }
+
+ if (!context.Settings.FullyQualifyAmbiguousTypeNames)
+ return;
+
+ FindAmbiguousTypeNames(context.CurrentModule, internalsVisible: true);
+ foreach (AssemblyNameReference r in context.CurrentModule.AssemblyReferences) {
+ AssemblyDefinition d = context.CurrentModule.AssemblyResolver.Resolve(r);
+ if (d != null)
+ FindAmbiguousTypeNames(d.MainModule, internalsVisible: false);
+ }
+
+ // verify that the SimpleTypes refer to the correct type (no ambiguities)
+ compilationUnit.AcceptVisitor(new FullyQualifyAmbiguousTypeNamesVisitor(this), null);
+ }
+
+ readonly HashSet<string> declaredNamespaces = new HashSet<string>() { string.Empty };
+ readonly HashSet<string> importedNamespaces = new HashSet<string>();
+
+ // Note that we store type names with `n suffix, so we automatically disambiguate based on number of type parameters.
+ readonly HashSet<string> availableTypeNames = new HashSet<string>();
+ readonly HashSet<string> ambiguousTypeNames = new HashSet<string>();
+
+ sealed class FindRequiredImports : DepthFirstAstVisitor<object, object>
+ {
+ readonly IntroduceUsingDeclarations transform;
+ string currentNamespace;
+
+ public FindRequiredImports(IntroduceUsingDeclarations transform)
+ {
+ this.transform = transform;
+ currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty;
+ }
+
+ bool IsParentOfCurrentNamespace(string ns)
+ {
+ if (ns.Length == 0)
+ return true;
+ if (currentNamespace.StartsWith(ns, StringComparison.Ordinal)) {
+ if (currentNamespace.Length == ns.Length)
+ return true;
+ if (currentNamespace[ns.Length] == '.')
+ return true;
+ }
+ return false;
+ }
+
+ public override object VisitSimpleType(SimpleType simpleType, object data)
+ {
+ TypeReference tr = simpleType.Annotation<TypeReference>();
+ if (tr != null && !IsParentOfCurrentNamespace(tr.Namespace)) {
+ transform.importedNamespaces.Add(tr.Namespace);
+ }
+ return base.VisitSimpleType(simpleType, data); // also visit type arguments
+ }
+
+ public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
+ {
+ string oldNamespace = currentNamespace;
+ foreach (string ident in namespaceDeclaration.Identifiers) {
+ currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident);
+ transform.declaredNamespaces.Add(currentNamespace);
+ }
+ base.VisitNamespaceDeclaration(namespaceDeclaration, data);
+ currentNamespace = oldNamespace;
+ return null;
+ }
+ }
+
+ void FindAmbiguousTypeNames(ModuleDefinition module, bool internalsVisible)
+ {
+ foreach (TypeDefinition type in module.Types) {
+ if (internalsVisible || type.IsPublic) {
+ if (importedNamespaces.Contains(type.Namespace) || declaredNamespaces.Contains(type.Namespace)) {
+ if (!availableTypeNames.Add(type.Name))
+ ambiguousTypeNames.Add(type.Name);
+ }
+ }
+ }
+ }
+
+ sealed class FullyQualifyAmbiguousTypeNamesVisitor : DepthFirstAstVisitor<object, object>
+ {
+ readonly IntroduceUsingDeclarations transform;
+ string currentNamespace;
+ HashSet<string> currentMemberTypes;
+ Dictionary<string, MemberReference> currentMembers;
+ bool isWithinTypeReferenceExpression;
+
+ public FullyQualifyAmbiguousTypeNamesVisitor(IntroduceUsingDeclarations transform)
+ {
+ this.transform = transform;
+ currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty;
+ }
+
+ public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
+ {
+ string oldNamespace = currentNamespace;
+ foreach (string ident in namespaceDeclaration.Identifiers) {
+ currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident);
+ }
+ base.VisitNamespaceDeclaration(namespaceDeclaration, data);
+ currentNamespace = oldNamespace;
+ return null;
+ }
+
+ public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
+ {
+ HashSet<string> oldMemberTypes = currentMemberTypes;
+ currentMemberTypes = currentMemberTypes != null ? new HashSet<string>(currentMemberTypes) : new HashSet<string>();
+
+ Dictionary<string, MemberReference> oldMembers = currentMembers;
+ currentMembers = new Dictionary<string, MemberReference>();
+
+ TypeDefinition typeDef = typeDeclaration.Annotation<TypeDefinition>();
+ bool privateMembersVisible = true;
+ ModuleDefinition internalMembersVisibleInModule = typeDef.Module;
+ while (typeDef != null) {
+ foreach (GenericParameter gp in typeDef.GenericParameters) {
+ currentMemberTypes.Add(gp.Name);
+ }
+ foreach (TypeDefinition t in typeDef.NestedTypes) {
+ if (privateMembersVisible || IsVisible(t, internalMembersVisibleInModule))
+ currentMemberTypes.Add(t.Name.Substring(t.Name.LastIndexOf('+') + 1));
+ }
+
+ foreach (MethodDefinition method in typeDef.Methods) {
+ if (privateMembersVisible || IsVisible(method, internalMembersVisibleInModule))
+ AddCurrentMember(method);
+ }
+ foreach (PropertyDefinition property in typeDef.Properties) {
+ if (privateMembersVisible || IsVisible(property.GetMethod, internalMembersVisibleInModule) || IsVisible(property.SetMethod, internalMembersVisibleInModule))
+ AddCurrentMember(property);
+ }
+ foreach (EventDefinition ev in typeDef.Events) {
+ if (privateMembersVisible || IsVisible(ev.AddMethod, internalMembersVisibleInModule) || IsVisible(ev.RemoveMethod, internalMembersVisibleInModule))
+ AddCurrentMember(ev);
+ }
+ foreach (FieldDefinition f in typeDef.Fields) {
+ if (privateMembersVisible || IsVisible(f, internalMembersVisibleInModule))
+ AddCurrentMember(f);
+ }
+ // repeat with base class:
+ if (typeDef.BaseType != null)
+ typeDef = typeDef.BaseType.Resolve();
+ else
+ typeDef = null;
+ privateMembersVisible = false;
+ }
+
+ // Now add current members from outer classes:
+ if (oldMembers != null) {
+ foreach (var pair in oldMembers) {
+ // add members from outer classes only if the inner class doesn't define the member
+ if (!currentMembers.ContainsKey(pair.Key))
+ currentMembers.Add(pair.Key, pair.Value);
+ }
+ }
+
+ base.VisitTypeDeclaration(typeDeclaration, data);
+ currentMembers = oldMembers;
+ return null;
+ }
+
+ void AddCurrentMember(MemberReference m)
+ {
+ MemberReference existingMember;
+ if (currentMembers.TryGetValue(m.Name, out existingMember)) {
+ // We keep the existing member assignment if it was from another class (=from a derived class),
+ // because members in derived classes have precedence over members in base classes.
+ if (existingMember != null && existingMember.DeclaringType == m.DeclaringType) {
+ // Use null as value to signalize multiple members with the same name
+ currentMembers[m.Name] = null;
+ }
+ } else {
+ currentMembers.Add(m.Name, m);
+ }
+ }
+
+ bool IsVisible(MethodDefinition m, ModuleDefinition internalMembersVisibleInModule)
+ {
+ if (m == null)
+ return false;
+ switch (m.Attributes & MethodAttributes.MemberAccessMask) {
+ case MethodAttributes.FamANDAssem:
+ case MethodAttributes.Assembly:
+ return m.Module == internalMembersVisibleInModule;
+ case MethodAttributes.Family:
+ case MethodAttributes.FamORAssem:
+ case MethodAttributes.Public:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool IsVisible(FieldDefinition f, ModuleDefinition internalMembersVisibleInModule)
+ {
+ if (f == null)
+ return false;
+ switch (f.Attributes & FieldAttributes.FieldAccessMask) {
+ case FieldAttributes.FamANDAssem:
+ case FieldAttributes.Assembly:
+ return f.Module == internalMembersVisibleInModule;
+ case FieldAttributes.Family:
+ case FieldAttributes.FamORAssem:
+ case FieldAttributes.Public:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool IsVisible(TypeDefinition t, ModuleDefinition internalMembersVisibleInModule)
+ {
+ if (t == null)
+ return false;
+ switch (t.Attributes & TypeAttributes.VisibilityMask) {
+ case TypeAttributes.NotPublic:
+ case TypeAttributes.NestedAssembly:
+ case TypeAttributes.NestedFamANDAssem:
+ return t.Module == internalMembersVisibleInModule;
+ case TypeAttributes.NestedFamily:
+ case TypeAttributes.NestedFamORAssem:
+ case TypeAttributes.NestedPublic:
+ case TypeAttributes.Public:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public override object VisitSimpleType(SimpleType simpleType, object data)
+ {
+ // Handle type arguments first, so that the fixed-up type arguments get moved over to the MemberType,
+ // if we're also creating one here.
+ base.VisitSimpleType(simpleType, data);
+ TypeReference tr = simpleType.Annotation<TypeReference>();
+ // Fully qualify any ambiguous type names.
+ if (tr != null && IsAmbiguous(tr.Namespace, tr.Name)) {
+ AstType ns;
+ if (string.IsNullOrEmpty(tr.Namespace)) {
+ ns = new SimpleType("global");
+ } else {
+ string[] parts = tr.Namespace.Split('.');
+ if (IsAmbiguous(string.Empty, parts[0])) {
+ // conflict between namespace and type name/member name
+ ns = new MemberType { Target = new SimpleType("global"), IsDoubleColon = true, MemberName = parts[0] };
+ } else {
+ ns = new SimpleType(parts[0]);
+ }
+ for (int i = 1; i < parts.Length; i++) {
+ ns = new MemberType { Target = ns, MemberName = parts[i] };
+ }
+ }
+ MemberType mt = new MemberType();
+ mt.Target = ns;
+ mt.IsDoubleColon = string.IsNullOrEmpty(tr.Namespace);
+ mt.MemberName = simpleType.Identifier;
+ mt.CopyAnnotationsFrom(simpleType);
+ simpleType.TypeArguments.MoveTo(mt.TypeArguments);
+ simpleType.ReplaceWith(mt);
+ }
+ return null;
+ }
+
+ public override object VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, object data)
+ {
+ isWithinTypeReferenceExpression = true;
+ base.VisitTypeReferenceExpression(typeReferenceExpression, data);
+ isWithinTypeReferenceExpression = false;
+ return null;
+ }
+
+ bool IsAmbiguous(string ns, string name)
+ {
+ // If the type name conflicts with an inner class/type parameter, we need to fully-qualify it:
+ if (currentMemberTypes != null && currentMemberTypes.Contains(name))
+ return true;
+ // If the type name conflicts with a field/property etc. on the current class, we need to fully-qualify it,
+ // if we're inside an expression.
+ if (isWithinTypeReferenceExpression && currentMembers != null) {
+ MemberReference mr;
+ if (currentMembers.TryGetValue(name, out mr)) {
+ // However, in the special case where the member is a field or property with the same type
+ // as is requested, then we can use the short name (if it's not otherwise ambiguous)
+ PropertyDefinition prop = mr as PropertyDefinition;
+ FieldDefinition field = mr as FieldDefinition;
+ if (!(prop != null && prop.PropertyType.Namespace == ns && prop.PropertyType.Name == name)
+ && !(field != null && field.FieldType.Namespace == ns && field.FieldType.Name == name))
+ return true;
+ }
+ }
+ // If the type is defined in the current namespace,
+ // then we can use the short name even if we imported type with same name from another namespace.
+ if (ns == currentNamespace && !string.IsNullOrEmpty(ns))
+ return false;
+ return transform.ambiguousTypeNames.Contains(name);
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
new file mode 100644
index 00000000..d3ae46c1
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
@@ -0,0 +1,1123 @@
+// 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.Generic;
+using System.Diagnostics;
+using System.Linq;
+
+using ICSharpCode.Decompiler.ILAst;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.CSharp.Analysis;
+using ICSharpCode.NRefactory.PatternMatching;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ /// <summary>
+ /// Finds the expanded form of using statements using pattern matching and replaces it with a UsingStatement.
+ /// </summary>
+ public sealed class PatternStatementTransform : ContextTrackingVisitor<AstNode>, IAstTransform
+ {
+ public PatternStatementTransform(DecompilerContext context) : base(context)
+ {
+ }
+
+ #region Visitor Overrides
+ protected override AstNode VisitChildren(AstNode node, object data)
+ {
+ // Go through the children, and keep visiting a node as long as it changes.
+ // Because some transforms delete/replace nodes before and after the node being transformed, we rely
+ // on the transform's return value to know where we need to keep iterating.
+ for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
+ AstNode oldChild;
+ do {
+ oldChild = child;
+ child = child.AcceptVisitor(this, data);
+ Debug.Assert(child != null && child.Parent == node);
+ } while (child != oldChild);
+ }
+ return node;
+ }
+
+ public override AstNode VisitExpressionStatement(ExpressionStatement expressionStatement, object data)
+ {
+ AstNode result;
+ if (context.Settings.UsingStatement) {
+ result = TransformUsings(expressionStatement);
+ if (result != null)
+ return result;
+ result = TransformNonGenericForEach(expressionStatement);
+ if (result != null)
+ return result;
+ }
+ result = TransformFor(expressionStatement);
+ if (result != null)
+ return result;
+ if (context.Settings.LockStatement) {
+ result = TransformLock(expressionStatement);
+ if (result != null)
+ return result;
+ }
+ return base.VisitExpressionStatement(expressionStatement, data);
+ }
+
+ public override AstNode VisitUsingStatement(UsingStatement usingStatement, object data)
+ {
+ if (context.Settings.ForEachStatement) {
+ AstNode result = TransformForeach(usingStatement);
+ if (result != null)
+ return result;
+ }
+ return base.VisitUsingStatement(usingStatement, data);
+ }
+
+ public override AstNode VisitWhileStatement(WhileStatement whileStatement, object data)
+ {
+ return TransformDoWhile(whileStatement) ?? base.VisitWhileStatement(whileStatement, data);
+ }
+
+ public override AstNode VisitIfElseStatement(IfElseStatement ifElseStatement, object data)
+ {
+ if (context.Settings.SwitchStatementOnString) {
+ AstNode result = TransformSwitchOnString(ifElseStatement);
+ if (result != null)
+ return result;
+ }
+ AstNode simplifiedIfElse = SimplifyCascadingIfElseStatements(ifElseStatement);
+ if (simplifiedIfElse != null)
+ return simplifiedIfElse;
+ return base.VisitIfElseStatement(ifElseStatement, data);
+ }
+
+ public override AstNode VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data)
+ {
+ if (context.Settings.AutomaticProperties) {
+ AstNode result = TransformAutomaticProperties(propertyDeclaration);
+ if (result != null)
+ return result;
+ }
+ return base.VisitPropertyDeclaration(propertyDeclaration, data);
+ }
+
+ public override AstNode VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration, object data)
+ {
+ // first apply transforms to the accessor bodies
+ base.VisitCustomEventDeclaration(eventDeclaration, data);
+ if (context.Settings.AutomaticEvents) {
+ AstNode result = TransformAutomaticEvents(eventDeclaration);
+ if (result != null)
+ return result;
+ }
+ return eventDeclaration;
+ }
+
+ public override AstNode VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
+ {
+ return TransformDestructor(methodDeclaration) ?? base.VisitMethodDeclaration(methodDeclaration, data);
+ }
+
+ public override AstNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement, object data)
+ {
+ return TransformTryCatchFinally(tryCatchStatement) ?? base.VisitTryCatchStatement(tryCatchStatement, data);
+ }
+ #endregion
+
+ /// <summary>
+ /// $variable = $initializer;
+ /// </summary>
+ static readonly AstNode variableAssignPattern = new ExpressionStatement(
+ new AssignmentExpression(
+ new NamedNode("variable", new IdentifierExpression(Pattern.AnyString)),
+ new AnyNode("initializer")
+ ));
+
+ #region using
+ static Expression InvokeDispose(Expression identifier)
+ {
+ return new Choice {
+ identifier.Invoke("Dispose"),
+ identifier.Clone().CastTo(new TypePattern(typeof(IDisposable))).Invoke("Dispose")
+ };
+ }
+
+ static readonly AstNode usingTryCatchPattern = new TryCatchStatement {
+ TryBlock = new AnyNode(),
+ FinallyBlock = new BlockStatement {
+ new Choice {
+ { "valueType",
+ new ExpressionStatement(InvokeDispose(new NamedNode("ident", new IdentifierExpression(Pattern.AnyString))))
+ },
+ { "referenceType",
+ new IfElseStatement {
+ Condition = new BinaryOperatorExpression(
+ new NamedNode("ident", new IdentifierExpression(Pattern.AnyString)),
+ BinaryOperatorType.InEquality,
+ new NullReferenceExpression()
+ ),
+ TrueStatement = new BlockStatement {
+ new ExpressionStatement(InvokeDispose(new Backreference("ident")))
+ }
+ }
+ }
+ }.ToStatement()
+ }
+ };
+
+ public UsingStatement TransformUsings(ExpressionStatement node)
+ {
+ Match m1 = variableAssignPattern.Match(node);
+ if (!m1.Success) return null;
+ TryCatchStatement tryCatch = node.NextSibling as TryCatchStatement;
+ Match m2 = usingTryCatchPattern.Match(tryCatch);
+ if (!m2.Success) return null;
+ string variableName = m1.Get<IdentifierExpression>("variable").Single().Identifier;
+ if (variableName != m2.Get<IdentifierExpression>("ident").Single().Identifier)
+ return null;
+ if (m2.Has("valueType")) {
+ // if there's no if(x!=null), then it must be a value type
+ ILVariable v = m1.Get<AstNode>("variable").Single().Annotation<ILVariable>();
+ if (v == null || v.Type == null || !v.Type.IsValueType)
+ return null;
+ }
+
+ // There are two variants of the using statement:
+ // "using (var a = init)" and "using (expr)".
+ // The former declares a read-only variable 'a', and the latter declares an unnamed read-only variable
+ // to store the original value of 'expr'.
+ // This means that in order to introduce a using statement, in both cases we need to detect a read-only
+ // variable that is used only within that block.
+
+ if (HasAssignment(tryCatch, variableName))
+ return null;
+
+ VariableDeclarationStatement varDecl = FindVariableDeclaration(node, variableName);
+ if (varDecl == null || !(varDecl.Parent is BlockStatement))
+ return null;
+
+ // Validate that the variable is not used after the using statement:
+ if (!IsVariableValueUnused(varDecl, tryCatch))
+ return null;
+
+ node.Remove();
+
+ UsingStatement usingStatement = new UsingStatement();
+ usingStatement.EmbeddedStatement = tryCatch.TryBlock.Detach();
+ tryCatch.ReplaceWith(usingStatement);
+
+ // If possible, we'll eliminate the variable completely:
+ if (usingStatement.EmbeddedStatement.Descendants.OfType<IdentifierExpression>().Any(ident => ident.Identifier == variableName)) {
+ // variable is used, so we'll create a variable declaration
+ usingStatement.ResourceAcquisition = new VariableDeclarationStatement {
+ Type = (AstType)varDecl.Type.Clone(),
+ Variables = {
+ new VariableInitializer {
+ Name = variableName,
+ Initializer = m1.Get<Expression>("initializer").Single().Detach()
+ }.CopyAnnotationsFrom(node.Expression)
+ .WithAnnotation(m1.Get<AstNode>("variable").Single().Annotation<ILVariable>())
+ }
+ }.CopyAnnotationsFrom(node);
+ } else {
+ // the variable is never used; eliminate it:
+ usingStatement.ResourceAcquisition = m1.Get<Expression>("initializer").Single().Detach();
+ }
+ return usingStatement;
+ }
+
+ internal static VariableDeclarationStatement FindVariableDeclaration(AstNode node, string identifier)
+ {
+ while (node != null) {
+ while (node.PrevSibling != null) {
+ node = node.PrevSibling;
+ VariableDeclarationStatement varDecl = node as VariableDeclarationStatement;
+ if (varDecl != null && varDecl.Variables.Count == 1 && varDecl.Variables.Single().Name == identifier) {
+ return varDecl;
+ }
+ }
+ node = node.Parent;
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Gets whether the old variable value (assigned inside 'targetStatement' or earlier)
+ /// is read anywhere in the remaining scope of the variable declaration.
+ /// </summary>
+ bool IsVariableValueUnused(VariableDeclarationStatement varDecl, Statement targetStatement)
+ {
+ Debug.Assert(targetStatement.Ancestors.Contains(varDecl.Parent));
+ BlockStatement block = (BlockStatement)varDecl.Parent;
+ DefiniteAssignmentAnalysis daa = new DefiniteAssignmentAnalysis(block, context.CancellationToken);
+ daa.SetAnalyzedRange(targetStatement, block, startInclusive: false);
+ daa.Analyze(varDecl.Variables.Single().Name);
+ return daa.UnassignedVariableUses.Count == 0;
+ }
+
+ // I used this in the first implementation of the using-statement transform, but now no longer
+ // because there were problems when multiple using statements were using the same variable
+ // - no single using statement could be transformed without making the C# code invalid,
+ // but transforming both would work.
+ // We now use 'IsVariableValueUnused' which will perform the transform
+ // even if it results in two variables with the same name and overlapping scopes.
+ // (this issue could be fixed later by renaming one of the variables)
+
+ // I'm not sure whether the other consumers of 'CanMoveVariableDeclarationIntoStatement' should be changed the same way.
+ bool CanMoveVariableDeclarationIntoStatement(VariableDeclarationStatement varDecl, Statement targetStatement, out Statement declarationPoint)
+ {
+ Debug.Assert(targetStatement.Ancestors.Contains(varDecl.Parent));
+ // Find all blocks between targetStatement and varDecl.Parent
+ List<BlockStatement> blocks = targetStatement.Ancestors.TakeWhile(block => block != varDecl.Parent).OfType<BlockStatement>().ToList();
+ blocks.Add((BlockStatement)varDecl.Parent); // also handle the varDecl.Parent block itself
+ blocks.Reverse(); // go from parent blocks to child blocks
+ DefiniteAssignmentAnalysis daa = new DefiniteAssignmentAnalysis(blocks[0], context.CancellationToken);
+ declarationPoint = null;
+ foreach (BlockStatement block in blocks) {
+ if (!DeclareVariables.FindDeclarationPoint(daa, varDecl, block, out declarationPoint)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Gets whether there is an assignment to 'variableName' anywhere within the given node.
+ /// </summary>
+ bool HasAssignment(AstNode root, string variableName)
+ {
+ foreach (AstNode node in root.DescendantsAndSelf) {
+ IdentifierExpression ident = node as IdentifierExpression;
+ if (ident != null && ident.Identifier == variableName) {
+ if (ident.Parent is AssignmentExpression && ident.Role == AssignmentExpression.LeftRole
+ || ident.Parent is DirectionExpression)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ #endregion
+
+ #region foreach (generic)
+ static readonly UsingStatement genericForeachPattern = new UsingStatement {
+ ResourceAcquisition = new VariableDeclarationStatement {
+ Type = new AnyNode("enumeratorType"),
+ Variables = {
+ new NamedNode(
+ "enumeratorVariable",
+ new VariableInitializer {
+ Name = Pattern.AnyString,
+ Initializer = new AnyNode("collection").ToExpression().Invoke("GetEnumerator")
+ }
+ )
+ }
+ },
+ EmbeddedStatement = new BlockStatement {
+ new Repeat(
+ new VariableDeclarationStatement { Type = new AnyNode(), Variables = { new VariableInitializer(Pattern.AnyString) } }.WithName("variablesOutsideLoop")
+ ).ToStatement(),
+ new WhileStatement {
+ Condition = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Invoke("MoveNext"),
+ EmbeddedStatement = new BlockStatement {
+ new Repeat(
+ new VariableDeclarationStatement {
+ Type = new AnyNode(),
+ Variables = { new VariableInitializer(Pattern.AnyString) }
+ }.WithName("variablesInsideLoop")
+ ).ToStatement(),
+ new AssignmentExpression {
+ Left = new IdentifierExpression(Pattern.AnyString).WithName("itemVariable"),
+ Operator = AssignmentOperatorType.Assign,
+ Right = new IdentifierExpressionBackreference("enumeratorVariable").ToExpression().Member("Current")
+ },
+ new Repeat(new AnyNode("statement")).ToStatement()
+ }
+ }.WithName("loop")
+ }};
+
+ public ForeachStatement TransformForeach(UsingStatement node)
+ {
+ Match m = genericForeachPattern.Match(node);
+ if (!m.Success)
+ return null;
+ if (!(node.Parent is BlockStatement) && m.Has("variablesOutsideLoop")) {
+ // if there are variables outside the loop, we need to put those into the parent block, and that won't work if the direct parent isn't a block
+ return null;
+ }
+ VariableInitializer enumeratorVar = m.Get<VariableInitializer>("enumeratorVariable").Single();
+ IdentifierExpression itemVar = m.Get<IdentifierExpression>("itemVariable").Single();
+ WhileStatement loop = m.Get<WhileStatement>("loop").Single();
+
+ // Find the declaration of the item variable:
+ // Because we look only outside the loop, we won't make the mistake of moving a captured variable across the loop boundary
+ VariableDeclarationStatement itemVarDecl = FindVariableDeclaration(loop, itemVar.Identifier);
+ if (itemVarDecl == null || !(itemVarDecl.Parent is BlockStatement))
+ return null;
+
+ // Now verify that we can move the variable declaration in front of the loop:
+ Statement declarationPoint;
+ CanMoveVariableDeclarationIntoStatement(itemVarDecl, loop, out declarationPoint);
+ // We ignore the return value because we don't care whether we can move the variable into the loop
+ // (that is possible only with non-captured variables).
+ // We just care that we can move it in front of the loop:
+ if (declarationPoint != loop)
+ return null;
+
+ BlockStatement newBody = new BlockStatement();
+ foreach (Statement stmt in m.Get<Statement>("variablesInsideLoop"))
+ newBody.Add(stmt.Detach());
+ foreach (Statement stmt in m.Get<Statement>("statement"))
+ newBody.Add(stmt.Detach());
+
+ ForeachStatement foreachStatement = new ForeachStatement {
+ VariableType = (AstType)itemVarDecl.Type.Clone(),
+ VariableName = itemVar.Identifier,
+ InExpression = m.Get<Expression>("collection").Single().Detach(),
+ EmbeddedStatement = newBody
+ }.WithAnnotation(itemVarDecl.Variables.Single().Annotation<ILVariable>());
+ if (foreachStatement.InExpression is BaseReferenceExpression) {
+ foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression);
+ }
+ node.ReplaceWith(foreachStatement);
+ foreach (Statement stmt in m.Get<Statement>("variablesOutsideLoop")) {
+ ((BlockStatement)foreachStatement.Parent).Statements.InsertAfter(null, stmt.Detach());
+ }
+ return foreachStatement;
+ }
+ #endregion
+
+ #region foreach (non-generic)
+ ExpressionStatement getEnumeratorPattern = new ExpressionStatement(
+ new AssignmentExpression(
+ new NamedNode("left", new IdentifierExpression(Pattern.AnyString)),
+ new AnyNode("collection").ToExpression().Invoke("GetEnumerator")
+ ));
+
+ TryCatchStatement nonGenericForeachPattern = new TryCatchStatement {
+ TryBlock = new BlockStatement {
+ new WhileStatement {
+ Condition = new IdentifierExpression(Pattern.AnyString).WithName("enumerator").Invoke("MoveNext"),
+ EmbeddedStatement = new BlockStatement {
+ new AssignmentExpression(
+ new IdentifierExpression(Pattern.AnyString).WithName("itemVar"),
+ new Choice {
+ new Backreference("enumerator").ToExpression().Member("Current"),
+ new CastExpression {
+ Type = new AnyNode("castType"),
+ Expression = new Backreference("enumerator").ToExpression().Member("Current")
+ }
+ }
+ ),
+ new Repeat(new AnyNode("stmt")).ToStatement()
+ }
+ }.WithName("loop")
+ },
+ FinallyBlock = new BlockStatement {
+ new AssignmentExpression(
+ new IdentifierExpression(Pattern.AnyString).WithName("disposable"),
+ new Backreference("enumerator").ToExpression().CastAs(new TypePattern(typeof(IDisposable)))
+ ),
+ new IfElseStatement {
+ Condition = new BinaryOperatorExpression {
+ Left = new Backreference("disposable"),
+ Operator = BinaryOperatorType.InEquality,
+ Right = new NullReferenceExpression()
+ },
+ TrueStatement = new BlockStatement {
+ new Backreference("disposable").ToExpression().Invoke("Dispose")
+ }
+ }
+ }};
+
+ public ForeachStatement TransformNonGenericForEach(ExpressionStatement node)
+ {
+ Match m1 = getEnumeratorPattern.Match(node);
+ if (!m1.Success) return null;
+ AstNode tryCatch = node.NextSibling;
+ Match m2 = nonGenericForeachPattern.Match(tryCatch);
+ if (!m2.Success) return null;
+
+ IdentifierExpression enumeratorVar = m2.Get<IdentifierExpression>("enumerator").Single();
+ IdentifierExpression itemVar = m2.Get<IdentifierExpression>("itemVar").Single();
+ WhileStatement loop = m2.Get<WhileStatement>("loop").Single();
+
+ // verify that the getEnumeratorPattern assigns to the same variable as the nonGenericForeachPattern is reading from
+ if (!enumeratorVar.IsMatch(m1.Get("left").Single()))
+ return null;
+
+ VariableDeclarationStatement enumeratorVarDecl = FindVariableDeclaration(loop, enumeratorVar.Identifier);
+ if (enumeratorVarDecl == null || !(enumeratorVarDecl.Parent is BlockStatement))
+ return null;
+
+ // Find the declaration of the item variable:
+ // Because we look only outside the loop, we won't make the mistake of moving a captured variable across the loop boundary
+ VariableDeclarationStatement itemVarDecl = FindVariableDeclaration(loop, itemVar.Identifier);
+ if (itemVarDecl == null || !(itemVarDecl.Parent is BlockStatement))
+ return null;
+
+ // Now verify that we can move the variable declaration in front of the loop:
+ Statement declarationPoint;
+ CanMoveVariableDeclarationIntoStatement(itemVarDecl, loop, out declarationPoint);
+ // We ignore the return value because we don't care whether we can move the variable into the loop
+ // (that is possible only with non-captured variables).
+ // We just care that we can move it in front of the loop:
+ if (declarationPoint != loop)
+ return null;
+
+ ForeachStatement foreachStatement = new ForeachStatement
+ {
+ VariableType = itemVarDecl.Type.Clone(),
+ VariableName = itemVar.Identifier,
+ }.WithAnnotation(itemVarDecl.Variables.Single().Annotation<ILVariable>());
+ BlockStatement body = new BlockStatement();
+ foreachStatement.EmbeddedStatement = body;
+ ((BlockStatement)node.Parent).Statements.InsertBefore(node, foreachStatement);
+
+ body.Add(node.Detach());
+ body.Add((Statement)tryCatch.Detach());
+
+ // Now that we moved the whole try-catch into the foreach loop; verify that we can
+ // move the enumerator into the foreach loop:
+ CanMoveVariableDeclarationIntoStatement(enumeratorVarDecl, foreachStatement, out declarationPoint);
+ if (declarationPoint != foreachStatement) {
+ // oops, the enumerator variable can't be moved into the foreach loop
+ // Undo our AST changes:
+ ((BlockStatement)foreachStatement.Parent).Statements.InsertBefore(foreachStatement, node.Detach());
+ foreachStatement.ReplaceWith(tryCatch);
+ return null;
+ }
+
+ // Now create the correct body for the foreach statement:
+ foreachStatement.InExpression = m1.Get<Expression>("collection").Single().Detach();
+ if (foreachStatement.InExpression is BaseReferenceExpression) {
+ foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression);
+ }
+ body.Statements.Clear();
+ body.Statements.AddRange(m2.Get<Statement>("stmt").Select(stmt => stmt.Detach()));
+
+ return foreachStatement;
+ }
+ #endregion
+
+ #region for
+ static readonly WhileStatement forPattern = new WhileStatement {
+ Condition = new BinaryOperatorExpression {
+ Left = new NamedNode("ident", new IdentifierExpression(Pattern.AnyString)),
+ Operator = BinaryOperatorType.Any,
+ Right = new AnyNode("endExpr")
+ },
+ EmbeddedStatement = new BlockStatement {
+ Statements = {
+ new Repeat(new AnyNode("statement")),
+ new NamedNode(
+ "increment",
+ new ExpressionStatement(
+ new AssignmentExpression {
+ Left = new Backreference("ident"),
+ Operator = AssignmentOperatorType.Any,
+ Right = new AnyNode()
+ }))
+ }
+ }};
+
+ public ForStatement TransformFor(ExpressionStatement node)
+ {
+ Match m1 = variableAssignPattern.Match(node);
+ if (!m1.Success) return null;
+ AstNode next = node.NextSibling;
+ Match m2 = forPattern.Match(next);
+ if (!m2.Success) return null;
+ // ensure the variable in the for pattern is the same as in the declaration
+ if (m1.Get<IdentifierExpression>("variable").Single().Identifier != m2.Get<IdentifierExpression>("ident").Single().Identifier)
+ return null;
+ WhileStatement loop = (WhileStatement)next;
+ node.Remove();
+ BlockStatement newBody = new BlockStatement();
+ foreach (Statement stmt in m2.Get<Statement>("statement"))
+ newBody.Add(stmt.Detach());
+ ForStatement forStatement = new ForStatement();
+ forStatement.Initializers.Add(node);
+ forStatement.Condition = loop.Condition.Detach();
+ forStatement.Iterators.Add(m2.Get<Statement>("increment").Single().Detach());
+ forStatement.EmbeddedStatement = newBody;
+ loop.ReplaceWith(forStatement);
+ return forStatement;
+ }
+ #endregion
+
+ #region doWhile
+ static readonly WhileStatement doWhilePattern = new WhileStatement {
+ Condition = new PrimitiveExpression(true),
+ EmbeddedStatement = new BlockStatement {
+ Statements = {
+ new Repeat(new AnyNode("statement")),
+ new IfElseStatement {
+ Condition = new AnyNode("condition"),
+ TrueStatement = new BlockStatement { new BreakStatement() }
+ }
+ }
+ }};
+
+ public DoWhileStatement TransformDoWhile(WhileStatement whileLoop)
+ {
+ Match m = doWhilePattern.Match(whileLoop);
+ if (m.Success) {
+ DoWhileStatement doLoop = new DoWhileStatement();
+ doLoop.Condition = new UnaryOperatorExpression(UnaryOperatorType.Not, m.Get<Expression>("condition").Single().Detach());
+ doLoop.Condition.AcceptVisitor(new PushNegation(), null);
+ BlockStatement block = (BlockStatement)whileLoop.EmbeddedStatement;
+ block.Statements.Last().Remove(); // remove if statement
+ doLoop.EmbeddedStatement = block.Detach();
+ whileLoop.ReplaceWith(doLoop);
+
+ // we may have to extract variable definitions out of the loop if they were used in the condition:
+ foreach (var varDecl in block.Statements.OfType<VariableDeclarationStatement>()) {
+ VariableInitializer v = varDecl.Variables.Single();
+ if (doLoop.Condition.DescendantsAndSelf.OfType<IdentifierExpression>().Any(i => i.Identifier == v.Name)) {
+ AssignmentExpression assign = new AssignmentExpression(new IdentifierExpression(v.Name), v.Initializer.Detach());
+ // move annotations from v to assign:
+ assign.CopyAnnotationsFrom(v);
+ v.RemoveAnnotations<object>();
+ // remove varDecl with assignment; and move annotations from varDecl to the ExpressionStatement:
+ varDecl.ReplaceWith(new ExpressionStatement(assign).CopyAnnotationsFrom(varDecl));
+ varDecl.RemoveAnnotations<object>();
+
+ // insert the varDecl above the do-while loop:
+ doLoop.Parent.InsertChildBefore(doLoop, varDecl, BlockStatement.StatementRole);
+ }
+ }
+ return doLoop;
+ }
+ return null;
+ }
+ #endregion
+
+ #region lock
+ static readonly AstNode lockFlagInitPattern = new ExpressionStatement(
+ new AssignmentExpression(
+ new NamedNode("variable", new IdentifierExpression(Pattern.AnyString)),
+ new PrimitiveExpression(false)
+ ));
+
+ static readonly AstNode lockTryCatchPattern = new TryCatchStatement {
+ TryBlock = new BlockStatement {
+ new OptionalNode(new VariableDeclarationStatement()).ToStatement(),
+ new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke(
+ "Enter", new AnyNode("enter"),
+ new DirectionExpression {
+ FieldDirection = FieldDirection.Ref,
+ Expression = new NamedNode("flag", new IdentifierExpression(Pattern.AnyString))
+ }),
+ new Repeat(new AnyNode()).ToStatement()
+ },
+ FinallyBlock = new BlockStatement {
+ new IfElseStatement {
+ Condition = new Backreference("flag"),
+ TrueStatement = new BlockStatement {
+ new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Exit", new AnyNode("exit"))
+ }
+ }
+ }};
+
+ static readonly AstNode oldMonitorCallPattern = new ExpressionStatement(
+ new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Enter", new AnyNode("enter"))
+ );
+
+ static readonly AstNode oldLockTryCatchPattern = new TryCatchStatement
+ {
+ TryBlock = new BlockStatement {
+ new Repeat(new AnyNode()).ToStatement()
+ },
+ FinallyBlock = new BlockStatement {
+ new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Exit", new AnyNode("exit"))
+ }
+ };
+
+ bool AnalyzeLockV2(ExpressionStatement node, out Expression enter, out Expression exit)
+ {
+ enter = null;
+ exit = null;
+ Match m1 = oldMonitorCallPattern.Match(node);
+ if (!m1.Success) return false;
+ Match m2 = oldLockTryCatchPattern.Match(node.NextSibling);
+ if (!m2.Success) return false;
+ enter = m1.Get<Expression>("enter").Single();
+ exit = m2.Get<Expression>("exit").Single();
+ return true;
+ }
+
+ bool AnalyzeLockV4(ExpressionStatement node, out Expression enter, out Expression exit)
+ {
+ enter = null;
+ exit = null;
+ Match m1 = lockFlagInitPattern.Match(node);
+ if (!m1.Success) return false;
+ Match m2 = lockTryCatchPattern.Match(node.NextSibling);
+ if (!m2.Success) return false;
+ enter = m2.Get<Expression>("enter").Single();
+ exit = m2.Get<Expression>("exit").Single();
+ return m1.Get<IdentifierExpression>("variable").Single().Identifier == m2.Get<IdentifierExpression>("flag").Single().Identifier;
+ }
+
+ public LockStatement TransformLock(ExpressionStatement node)
+ {
+ Expression enter, exit;
+ bool isV2 = AnalyzeLockV2(node, out enter, out exit);
+ if (isV2 || AnalyzeLockV4(node, out enter, out exit)) {
+ AstNode tryCatch = node.NextSibling;
+ if (!exit.IsMatch(enter)) {
+ // If exit and enter are not the same, then enter must be "exit = ..."
+ AssignmentExpression assign = enter as AssignmentExpression;
+ if (assign == null)
+ return null;
+ if (!exit.IsMatch(assign.Left))
+ return null;
+ enter = assign.Right;
+ // TODO: verify that 'obj' variable can be removed
+ }
+ // TODO: verify that 'flag' variable can be removed
+ // transform the code into a lock statement:
+ LockStatement l = new LockStatement();
+ l.Expression = enter.Detach();
+ l.EmbeddedStatement = ((TryCatchStatement)tryCatch).TryBlock.Detach();
+ if (!isV2) // Remove 'Enter()' call
+ ((BlockStatement)l.EmbeddedStatement).Statements.First().Remove();
+ tryCatch.ReplaceWith(l);
+ node.Remove(); // remove flag variable
+ return l;
+ }
+ return null;
+ }
+ #endregion
+
+ #region switch on strings
+ static readonly IfElseStatement switchOnStringPattern = new IfElseStatement {
+ Condition = new BinaryOperatorExpression {
+ Left = new AnyNode("switchExpr"),
+ Operator = BinaryOperatorType.InEquality,
+ Right = new NullReferenceExpression()
+ },
+ TrueStatement = new BlockStatement {
+ new IfElseStatement {
+ Condition = new BinaryOperatorExpression {
+ Left = new AnyNode("cachedDict"),
+ Operator = BinaryOperatorType.Equality,
+ Right = new NullReferenceExpression()
+ },
+ TrueStatement = new AnyNode("dictCreation")
+ },
+ new IfElseStatement {
+ Condition = new Backreference("cachedDict").ToExpression().Invoke(
+ "TryGetValue",
+ new NamedNode("switchVar", new IdentifierExpression(Pattern.AnyString)),
+ new DirectionExpression {
+ FieldDirection = FieldDirection.Out,
+ Expression = new IdentifierExpression(Pattern.AnyString).WithName("intVar")
+ }),
+ TrueStatement = new BlockStatement {
+ Statements = {
+ new NamedNode(
+ "switch", new SwitchStatement {
+ Expression = new IdentifierExpressionBackreference("intVar"),
+ SwitchSections = { new Repeat(new AnyNode()) }
+ })
+ }
+ }
+ },
+ new Repeat(new AnyNode("nonNullDefaultStmt")).ToStatement()
+ },
+ FalseStatement = new OptionalNode("nullStmt", new BlockStatement { Statements = { new Repeat(new AnyNode()) } })
+ };
+
+ public SwitchStatement TransformSwitchOnString(IfElseStatement node)
+ {
+ Match m = switchOnStringPattern.Match(node);
+ if (!m.Success)
+ return null;
+ // switchVar must be the same as switchExpr; or switchExpr must be an assignment and switchVar the left side of that assignment
+ if (!m.Get("switchVar").Single().IsMatch(m.Get("switchExpr").Single())) {
+ AssignmentExpression assign = m.Get("switchExpr").Single() as AssignmentExpression;
+ if (!(assign != null && m.Get("switchVar").Single().IsMatch(assign.Left)))
+ return null;
+ }
+ FieldReference cachedDictField = m.Get<AstNode>("cachedDict").Single().Annotation<FieldReference>();
+ if (cachedDictField == null)
+ return null;
+ List<Statement> dictCreation = m.Get<BlockStatement>("dictCreation").Single().Statements.ToList();
+ List<KeyValuePair<string, int>> dict = BuildDictionary(dictCreation);
+ SwitchStatement sw = m.Get<SwitchStatement>("switch").Single();
+ sw.Expression = m.Get<Expression>("switchExpr").Single().Detach();
+ foreach (SwitchSection section in sw.SwitchSections) {
+ List<CaseLabel> labels = section.CaseLabels.ToList();
+ section.CaseLabels.Clear();
+ foreach (CaseLabel label in labels) {
+ PrimitiveExpression expr = label.Expression as PrimitiveExpression;
+ if (expr == null || !(expr.Value is int))
+ continue;
+ int val = (int)expr.Value;
+ foreach (var pair in dict) {
+ if (pair.Value == val)
+ section.CaseLabels.Add(new CaseLabel { Expression = new PrimitiveExpression(pair.Key) });
+ }
+ }
+ }
+ if (m.Has("nullStmt")) {
+ SwitchSection section = new SwitchSection();
+ section.CaseLabels.Add(new CaseLabel { Expression = new NullReferenceExpression() });
+ BlockStatement block = m.Get<BlockStatement>("nullStmt").Single();
+ block.Statements.Add(new BreakStatement());
+ section.Statements.Add(block.Detach());
+ sw.SwitchSections.Add(section);
+ } else if (m.Has("nonNullDefaultStmt")) {
+ sw.SwitchSections.Add(
+ new SwitchSection {
+ CaseLabels = { new CaseLabel { Expression = new NullReferenceExpression() } },
+ Statements = { new BlockStatement { new BreakStatement() } }
+ });
+ }
+ if (m.Has("nonNullDefaultStmt")) {
+ SwitchSection section = new SwitchSection();
+ section.CaseLabels.Add(new CaseLabel());
+ BlockStatement block = new BlockStatement();
+ block.Statements.AddRange(m.Get<Statement>("nonNullDefaultStmt").Select(s => s.Detach()));
+ block.Add(new BreakStatement());
+ section.Statements.Add(block);
+ sw.SwitchSections.Add(section);
+ }
+ node.ReplaceWith(sw);
+ return sw;
+ }
+
+ List<KeyValuePair<string, int>> BuildDictionary(List<Statement> dictCreation)
+ {
+ if (context.Settings.ObjectOrCollectionInitializers && dictCreation.Count == 1)
+ return BuildDictionaryFromInitializer(dictCreation[0]);
+
+ return BuildDictionaryFromAddMethodCalls(dictCreation);
+ }
+
+ static readonly Statement assignInitializedDictionary = new ExpressionStatement {
+ Expression = new AssignmentExpression {
+ Left = new AnyNode().ToExpression(),
+ Right = new ObjectCreateExpression {
+ Type = new AnyNode(),
+ Arguments = { new Repeat(new AnyNode()) },
+ Initializer = new ArrayInitializerExpression {
+ Elements = { new Repeat(new AnyNode("dictJumpTable")) }
+ }
+ },
+ },
+ };
+
+ List<KeyValuePair<string, int>> BuildDictionaryFromInitializer(Statement statement)
+ {
+ List<KeyValuePair<string, int>> dict = new List<KeyValuePair<string, int>>();
+ Match m = assignInitializedDictionary.Match(statement);
+ if (!m.Success)
+ return dict;
+
+ foreach (ArrayInitializerExpression initializer in m.Get<ArrayInitializerExpression>("dictJumpTable")) {
+ KeyValuePair<string, int> pair;
+ if (TryGetPairFrom(initializer.Elements, out pair))
+ dict.Add(pair);
+ }
+
+ return dict;
+ }
+
+ static List<KeyValuePair<string, int>> BuildDictionaryFromAddMethodCalls(List<Statement> dictCreation)
+ {
+ List<KeyValuePair<string, int>> dict = new List<KeyValuePair<string, int>>();
+ for (int i = 0; i < dictCreation.Count; i++) {
+ ExpressionStatement es = dictCreation[i] as ExpressionStatement;
+ if (es == null)
+ continue;
+ InvocationExpression ie = es.Expression as InvocationExpression;
+ if (ie == null)
+ continue;
+
+ KeyValuePair<string, int> pair;
+ if (TryGetPairFrom(ie.Arguments, out pair))
+ dict.Add(pair);
+ }
+ return dict;
+ }
+
+ static bool TryGetPairFrom(AstNodeCollection<Expression> expressions, out KeyValuePair<string, int> pair)
+ {
+ PrimitiveExpression arg1 = expressions.ElementAtOrDefault(0) as PrimitiveExpression;
+ PrimitiveExpression arg2 = expressions.ElementAtOrDefault(1) as PrimitiveExpression;
+ if (arg1 != null && arg2 != null && arg1.Value is string && arg2.Value is int) {
+ pair = new KeyValuePair<string, int>((string)arg1.Value, (int)arg2.Value);
+ return true;
+ }
+
+ pair = default(KeyValuePair<string, int>);
+ return false;
+ }
+
+ #endregion
+
+ #region Automatic Properties
+ static readonly PropertyDeclaration automaticPropertyPattern = new PropertyDeclaration {
+ Attributes = { new Repeat(new AnyNode()) },
+ Modifiers = Modifiers.Any,
+ ReturnType = new AnyNode(),
+ PrivateImplementationType = new OptionalNode(new AnyNode()),
+ Name = Pattern.AnyString,
+ Getter = new Accessor {
+ Attributes = { new Repeat(new AnyNode()) },
+ Modifiers = Modifiers.Any,
+ Body = new BlockStatement {
+ new ReturnStatement {
+ Expression = new AnyNode("fieldReference")
+ }
+ }
+ },
+ Setter = new Accessor {
+ Attributes = { new Repeat(new AnyNode()) },
+ Modifiers = Modifiers.Any,
+ Body = new BlockStatement {
+ new AssignmentExpression {
+ Left = new Backreference("fieldReference"),
+ Right = new IdentifierExpression("value")
+ }
+ }}};
+
+ PropertyDeclaration TransformAutomaticProperties(PropertyDeclaration property)
+ {
+ PropertyDefinition cecilProperty = property.Annotation<PropertyDefinition>();
+ if (cecilProperty == null || cecilProperty.GetMethod == null || cecilProperty.SetMethod == null)
+ return null;
+ if (!(cecilProperty.GetMethod.IsCompilerGenerated() && cecilProperty.SetMethod.IsCompilerGenerated()))
+ return null;
+ Match m = automaticPropertyPattern.Match(property);
+ if (m.Success) {
+ FieldDefinition field = m.Get<AstNode>("fieldReference").Single().Annotation<FieldReference>().ResolveWithinSameModule();
+ if (field.IsCompilerGenerated() && field.DeclaringType == cecilProperty.DeclaringType) {
+ RemoveCompilerGeneratedAttribute(property.Getter.Attributes);
+ RemoveCompilerGeneratedAttribute(property.Setter.Attributes);
+ property.Getter.Body = null;
+ property.Setter.Body = null;
+ }
+ }
+ // Since the event instance is not changed, we can continue in the visitor as usual, so return null
+ return null;
+ }
+
+ void RemoveCompilerGeneratedAttribute(AstNodeCollection<AttributeSection> attributeSections)
+ {
+ foreach (AttributeSection section in attributeSections) {
+ foreach (var attr in section.Attributes) {
+ TypeReference tr = attr.Type.Annotation<TypeReference>();
+ if (tr != null && tr.Namespace == "System.Runtime.CompilerServices" && tr.Name == "CompilerGeneratedAttribute") {
+ attr.Remove();
+ }
+ }
+ if (section.Attributes.Count == 0)
+ section.Remove();
+ }
+ }
+ #endregion
+
+ #region Automatic Events
+ static readonly Accessor automaticEventPatternV4 = new Accessor {
+ Attributes = { new Repeat(new AnyNode()) },
+ Body = new BlockStatement {
+ new VariableDeclarationStatement { Type = new AnyNode("type"), Variables = { new AnyNode() } },
+ new VariableDeclarationStatement { Type = new Backreference("type"), Variables = { new AnyNode() } },
+ new VariableDeclarationStatement { Type = new Backreference("type"), Variables = { new AnyNode() } },
+ new AssignmentExpression {
+ Left = new NamedNode("var1", new IdentifierExpression(Pattern.AnyString)),
+ Operator = AssignmentOperatorType.Assign,
+ Right = new NamedNode(
+ "field",
+ new MemberReferenceExpression {
+ Target = new Choice { new ThisReferenceExpression(), new TypeReferenceExpression { Type = new AnyNode() } },
+ MemberName = Pattern.AnyString
+ })
+ },
+ new DoWhileStatement {
+ EmbeddedStatement = new BlockStatement {
+ new AssignmentExpression(new NamedNode("var2", new IdentifierExpression(Pattern.AnyString)), new IdentifierExpressionBackreference("var1")),
+ new AssignmentExpression {
+ Left = new NamedNode("var3", new IdentifierExpression(Pattern.AnyString)),
+ Operator = AssignmentOperatorType.Assign,
+ Right = new AnyNode("delegateCombine").ToExpression().Invoke(
+ new IdentifierExpressionBackreference("var2"),
+ new IdentifierExpression("value")
+ ).CastTo(new Backreference("type"))
+ },
+ new AssignmentExpression {
+ Left = new IdentifierExpressionBackreference("var1"),
+ Right = new TypePattern(typeof(System.Threading.Interlocked)).ToType().Invoke(
+ "CompareExchange",
+ new AstType[] { new Backreference("type") }, // type argument
+ new Expression[] { // arguments
+ new DirectionExpression { FieldDirection = FieldDirection.Ref, Expression = new Backreference("field") },
+ new IdentifierExpressionBackreference("var3"),
+ new IdentifierExpressionBackreference("var2")
+ }
+ )}
+ },
+ Condition = new BinaryOperatorExpression {
+ Left = new IdentifierExpressionBackreference("var1"),
+ Operator = BinaryOperatorType.InEquality,
+ Right = new IdentifierExpressionBackreference("var2")
+ }}
+ }};
+
+ bool CheckAutomaticEventV4Match(Match m, CustomEventDeclaration ev, bool isAddAccessor)
+ {
+ if (!m.Success)
+ return false;
+ if (m.Get<MemberReferenceExpression>("field").Single().MemberName != ev.Name)
+ return false; // field name must match event name
+ if (!ev.ReturnType.IsMatch(m.Get("type").Single()))
+ return false; // variable types must match event type
+ var combineMethod = m.Get<AstNode>("delegateCombine").Single().Parent.Annotation<MethodReference>();
+ if (combineMethod == null || combineMethod.Name != (isAddAccessor ? "Combine" : "Remove"))
+ return false;
+ return combineMethod.DeclaringType.FullName == "System.Delegate";
+ }
+
+ EventDeclaration TransformAutomaticEvents(CustomEventDeclaration ev)
+ {
+ Match m1 = automaticEventPatternV4.Match(ev.AddAccessor);
+ if (!CheckAutomaticEventV4Match(m1, ev, true))
+ return null;
+ Match m2 = automaticEventPatternV4.Match(ev.RemoveAccessor);
+ if (!CheckAutomaticEventV4Match(m2, ev, false))
+ return null;
+ EventDeclaration ed = new EventDeclaration();
+ ev.Attributes.MoveTo(ed.Attributes);
+ foreach (var attr in ev.AddAccessor.Attributes) {
+ attr.AttributeTarget = "method";
+ ed.Attributes.Add(attr.Detach());
+ }
+ ed.ReturnType = ev.ReturnType.Detach();
+ ed.Modifiers = ev.Modifiers;
+ ed.Variables.Add(new VariableInitializer(ev.Name));
+ ed.CopyAnnotationsFrom(ev);
+
+ EventDefinition eventDef = ev.Annotation<EventDefinition>();
+ if (eventDef != null) {
+ FieldDefinition field = eventDef.DeclaringType.Fields.FirstOrDefault(f => f.Name == ev.Name);
+ if (field != null) {
+ ed.AddAnnotation(field);
+ AstBuilder.ConvertAttributes(ed, field, "field");
+ }
+ }
+
+ ev.ReplaceWith(ed);
+ return ed;
+ }
+ #endregion
+
+ #region Destructor
+ static readonly MethodDeclaration destructorPattern = new MethodDeclaration {
+ Attributes = { new Repeat(new AnyNode()) },
+ Modifiers = Modifiers.Any,
+ ReturnType = new PrimitiveType("void"),
+ Name = "Finalize",
+ Body = new BlockStatement {
+ new TryCatchStatement {
+ TryBlock = new AnyNode("body"),
+ FinallyBlock = new BlockStatement {
+ new BaseReferenceExpression().Invoke("Finalize")
+ }
+ }
+ }
+ };
+
+ DestructorDeclaration TransformDestructor(MethodDeclaration methodDef)
+ {
+ Match m = destructorPattern.Match(methodDef);
+ if (m.Success) {
+ DestructorDeclaration dd = new DestructorDeclaration();
+ methodDef.Attributes.MoveTo(dd.Attributes);
+ dd.Modifiers = methodDef.Modifiers & ~(Modifiers.Protected | Modifiers.Override);
+ dd.Body = m.Get<BlockStatement>("body").Single().Detach();
+ dd.Name = AstBuilder.CleanName(context.CurrentType.Name);
+ methodDef.ReplaceWith(dd);
+ return dd;
+ }
+ return null;
+ }
+ #endregion
+
+ #region Try-Catch-Finally
+ static readonly TryCatchStatement tryCatchFinallyPattern = new TryCatchStatement {
+ TryBlock = new BlockStatement {
+ new TryCatchStatement {
+ TryBlock = new AnyNode(),
+ CatchClauses = { new Repeat(new AnyNode()) }
+ }
+ },
+ FinallyBlock = new AnyNode()
+ };
+
+ /// <summary>
+ /// Simplify nested 'try { try {} catch {} } finally {}'.
+ /// This transformation must run after the using/lock tranformations.
+ /// </summary>
+ TryCatchStatement TransformTryCatchFinally(TryCatchStatement tryFinally)
+ {
+ if (tryCatchFinallyPattern.IsMatch(tryFinally)) {
+ TryCatchStatement tryCatch = (TryCatchStatement)tryFinally.TryBlock.Statements.Single();
+ tryFinally.TryBlock = tryCatch.TryBlock.Detach();
+ tryCatch.CatchClauses.MoveTo(tryFinally.CatchClauses);
+ }
+ // Since the tryFinally instance is not changed, we can continue in the visitor as usual, so return null
+ return null;
+ }
+ #endregion
+
+ #region Simplify cascading if-else-if statements
+ static readonly IfElseStatement cascadingIfElsePattern = new IfElseStatement
+ {
+ Condition = new AnyNode(),
+ TrueStatement = new AnyNode(),
+ FalseStatement = new BlockStatement {
+ Statements = {
+ new NamedNode(
+ "nestedIfStatement",
+ new IfElseStatement {
+ Condition = new AnyNode(),
+ TrueStatement = new AnyNode(),
+ FalseStatement = new OptionalNode(new AnyNode())
+ }
+ )
+ }
+ }
+ };
+
+ AstNode SimplifyCascadingIfElseStatements(IfElseStatement node)
+ {
+ Match m = cascadingIfElsePattern.Match(node);
+ if (m.Success) {
+ IfElseStatement elseIf = m.Get<IfElseStatement>("nestedIfStatement").Single();
+ node.FalseStatement = elseIf.Detach();
+ }
+
+ return null;
+ }
+ #endregion
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs b/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs
new file mode 100644
index 00000000..193c5e69
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs
@@ -0,0 +1,164 @@
+// 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.Generic;
+using System.Linq;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.PatternMatching;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ public class PushNegation: DepthFirstAstVisitor<object, object>, IAstTransform
+ {
+ sealed class LiftedOperator { }
+ /// <summary>
+ /// Annotation for lifted operators that cannot be transformed by PushNegation
+ /// </summary>
+ public static readonly object LiftedOperatorAnnotation = new LiftedOperator();
+
+ public override object VisitUnaryOperatorExpression(UnaryOperatorExpression unary, object data)
+ {
+ // lifted operators can't be transformed
+ if (unary.Annotation<LiftedOperator>() != null || unary.Expression.Annotation<LiftedOperator>() != null)
+ return base.VisitUnaryOperatorExpression(unary, data);
+
+ // Remove double negation
+ // !!a
+ if (unary.Operator == UnaryOperatorType.Not &&
+ unary.Expression is UnaryOperatorExpression &&
+ (unary.Expression as UnaryOperatorExpression).Operator == UnaryOperatorType.Not)
+ {
+ AstNode newNode = (unary.Expression as UnaryOperatorExpression).Expression;
+ unary.ReplaceWith(newNode);
+ return newNode.AcceptVisitor(this, data);
+ }
+
+ // Push through binary operation
+ // !((a) op (b))
+ BinaryOperatorExpression binaryOp = unary.Expression as BinaryOperatorExpression;
+ if (unary.Operator == UnaryOperatorType.Not && binaryOp != null) {
+ bool successful = true;
+ switch (binaryOp.Operator) {
+ case BinaryOperatorType.Equality:
+ binaryOp.Operator = BinaryOperatorType.InEquality;
+ break;
+ case BinaryOperatorType.InEquality:
+ binaryOp.Operator = BinaryOperatorType.Equality;
+ break;
+ case BinaryOperatorType.GreaterThan: // TODO: these are invalid for floats (stupid NaN)
+ binaryOp.Operator = BinaryOperatorType.LessThanOrEqual;
+ break;
+ case BinaryOperatorType.GreaterThanOrEqual:
+ binaryOp.Operator = BinaryOperatorType.LessThan;
+ break;
+ case BinaryOperatorType.LessThanOrEqual:
+ binaryOp.Operator = BinaryOperatorType.GreaterThan;
+ break;
+ case BinaryOperatorType.LessThan:
+ binaryOp.Operator = BinaryOperatorType.GreaterThanOrEqual;
+ break;
+ default:
+ successful = false;
+ break;
+ }
+ if (successful) {
+ unary.ReplaceWith(binaryOp);
+ return binaryOp.AcceptVisitor(this, data);
+ }
+
+ successful = true;
+ switch (binaryOp.Operator) {
+ case BinaryOperatorType.ConditionalAnd:
+ binaryOp.Operator = BinaryOperatorType.ConditionalOr;
+ break;
+ case BinaryOperatorType.ConditionalOr:
+ binaryOp.Operator = BinaryOperatorType.ConditionalAnd;
+ break;
+ default:
+ successful = false;
+ break;
+ }
+ if (successful) {
+ binaryOp.Left.ReplaceWith(e => new UnaryOperatorExpression(UnaryOperatorType.Not, e));
+ binaryOp.Right.ReplaceWith(e => new UnaryOperatorExpression(UnaryOperatorType.Not, e));
+ unary.ReplaceWith(binaryOp);
+ return binaryOp.AcceptVisitor(this, data);
+ }
+ }
+ return base.VisitUnaryOperatorExpression(unary, data);
+ }
+
+ readonly static AstNode asCastIsNullPattern = new BinaryOperatorExpression(
+ new AnyNode("expr").ToExpression().CastAs(new AnyNode("type")),
+ BinaryOperatorType.Equality,
+ new NullReferenceExpression()
+ );
+
+ readonly static AstNode asCastIsNotNullPattern = new BinaryOperatorExpression(
+ new AnyNode("expr").ToExpression().CastAs(new AnyNode("type")),
+ BinaryOperatorType.InEquality,
+ new NullReferenceExpression()
+ );
+
+ public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data)
+ {
+ // lifted operators can't be transformed
+ if (binaryOperatorExpression.Annotation<LiftedOperator>() != null)
+ return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data);
+
+ BinaryOperatorType op = binaryOperatorExpression.Operator;
+ bool? rightOperand = null;
+ if (binaryOperatorExpression.Right is PrimitiveExpression)
+ rightOperand = ((PrimitiveExpression)binaryOperatorExpression.Right).Value as bool?;
+ if (op == BinaryOperatorType.Equality && rightOperand == true || op == BinaryOperatorType.InEquality && rightOperand == false) {
+ // 'b == true' or 'b != false' is useless
+ binaryOperatorExpression.Left.AcceptVisitor(this, data);
+ binaryOperatorExpression.ReplaceWith(binaryOperatorExpression.Left);
+ return null;
+ } else if (op == BinaryOperatorType.Equality && rightOperand == false || op == BinaryOperatorType.InEquality && rightOperand == true) {
+ // 'b == false' or 'b != true' is a negation:
+ Expression left = binaryOperatorExpression.Left;
+ left.Remove();
+ UnaryOperatorExpression uoe = new UnaryOperatorExpression(UnaryOperatorType.Not, left);
+ binaryOperatorExpression.ReplaceWith(uoe);
+ return uoe.AcceptVisitor(this, data);
+ } else {
+ bool negate = false;
+ Match m = asCastIsNotNullPattern.Match(binaryOperatorExpression);
+ if (!m.Success) {
+ m = asCastIsNullPattern.Match(binaryOperatorExpression);
+ negate = true;
+ }
+ if (m.Success) {
+ Expression expr = m.Get<Expression>("expr").Single().Detach().IsType(m.Get<AstType>("type").Single().Detach());
+ if (negate)
+ expr = new UnaryOperatorExpression(UnaryOperatorType.Not, expr);
+ binaryOperatorExpression.ReplaceWith(expr);
+ return expr.AcceptVisitor(this, data);
+ } else {
+ return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data);
+ }
+ }
+ }
+ void IAstTransform.Run(AstNode node)
+ {
+ node.AcceptVisitor(this, null);
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
new file mode 100644
index 00000000..6a3f8f97
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
@@ -0,0 +1,356 @@
+// 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.Generic;
+using System.Linq;
+using System.Reflection;
+using ICSharpCode.NRefactory.PatternMatching;
+using Mono.Cecil;
+using Ast = ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.CSharp;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ /// <summary>
+ /// Replaces method calls with the appropriate operator expressions.
+ /// Also simplifies "x = x op y" into "x op= y" where possible.
+ /// </summary>
+ public class ReplaceMethodCallsWithOperators : DepthFirstAstVisitor<object, object>, IAstTransform
+ {
+ static readonly MemberReferenceExpression typeHandleOnTypeOfPattern = new MemberReferenceExpression {
+ Target = new Choice {
+ new TypeOfExpression(new AnyNode()),
+ new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.RefType, Arguments = { new AnyNode() } }
+ },
+ MemberName = "TypeHandle"
+ };
+
+ DecompilerContext context;
+
+ public ReplaceMethodCallsWithOperators(DecompilerContext context)
+ {
+ this.context = context;
+ }
+
+ public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data)
+ {
+ base.VisitInvocationExpression(invocationExpression, data);
+ ProcessInvocationExpression(invocationExpression);
+ return null;
+ }
+
+ internal static void ProcessInvocationExpression(InvocationExpression invocationExpression)
+ {
+ MethodReference methodRef = invocationExpression.Annotation<MethodReference>();
+ if (methodRef == null)
+ return;
+ var arguments = invocationExpression.Arguments.ToArray();
+
+ // Reduce "String.Concat(a, b)" to "a + b"
+ if (methodRef.Name == "Concat" && methodRef.DeclaringType.FullName == "System.String" && arguments.Length >= 2)
+ {
+ invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
+ Expression expr = arguments[0];
+ for (int i = 1; i < arguments.Length; i++) {
+ expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arguments[i]);
+ }
+ invocationExpression.ReplaceWith(expr);
+ return;
+ }
+
+ switch (methodRef.FullName) {
+ case "System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)":
+ if (arguments.Length == 1) {
+ if (typeHandleOnTypeOfPattern.IsMatch(arguments[0])) {
+ invocationExpression.ReplaceWith(((MemberReferenceExpression)arguments[0]).Target);
+ return;
+ }
+ }
+ break;
+ case "System.Reflection.FieldInfo System.Reflection.FieldInfo::GetFieldFromHandle(System.RuntimeFieldHandle)":
+ if (arguments.Length == 1) {
+ MemberReferenceExpression mre = arguments[0] as MemberReferenceExpression;
+ if (mre != null && mre.MemberName == "FieldHandle" && mre.Target.Annotation<LdTokenAnnotation>() != null) {
+ invocationExpression.ReplaceWith(mre.Target);
+ return;
+ }
+ }
+ break;
+ case "System.Reflection.FieldInfo System.Reflection.FieldInfo::GetFieldFromHandle(System.RuntimeFieldHandle,System.RuntimeTypeHandle)":
+ if (arguments.Length == 2) {
+ MemberReferenceExpression mre1 = arguments[0] as MemberReferenceExpression;
+ MemberReferenceExpression mre2 = arguments[1] as MemberReferenceExpression;
+ if (mre1 != null && mre1.MemberName == "FieldHandle" && mre1.Target.Annotation<LdTokenAnnotation>() != null) {
+ if (mre2 != null && mre2.MemberName == "TypeHandle" && mre2.Target is TypeOfExpression) {
+ Expression oldArg = ((InvocationExpression)mre1.Target).Arguments.Single();
+ FieldReference field = oldArg.Annotation<FieldReference>();
+ if (field != null) {
+ AstType declaringType = ((TypeOfExpression)mre2.Target).Type.Detach();
+ oldArg.ReplaceWith(declaringType.Member(field.Name).WithAnnotation(field));
+ invocationExpression.ReplaceWith(mre1.Target);
+ return;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(methodRef.Name);
+ if (bop != null && arguments.Length == 2) {
+ invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
+ invocationExpression.ReplaceWith(
+ new BinaryOperatorExpression(arguments[0], bop.Value, arguments[1]).WithAnnotation(methodRef)
+ );
+ return;
+ }
+ UnaryOperatorType? uop = GetUnaryOperatorTypeFromMetadataName(methodRef.Name);
+ if (uop != null && arguments.Length == 1) {
+ arguments[0].Remove(); // detach argument
+ invocationExpression.ReplaceWith(
+ new UnaryOperatorExpression(uop.Value, arguments[0]).WithAnnotation(methodRef)
+ );
+ return;
+ }
+ if (methodRef.Name == "op_Explicit" && arguments.Length == 1) {
+ arguments[0].Remove(); // detach argument
+ invocationExpression.ReplaceWith(
+ arguments[0].CastTo(AstBuilder.ConvertType(methodRef.ReturnType, methodRef.MethodReturnType))
+ .WithAnnotation(methodRef)
+ );
+ return;
+ }
+ if (methodRef.Name == "op_Implicit" && arguments.Length == 1) {
+ invocationExpression.ReplaceWith(arguments[0]);
+ return;
+ }
+ if (methodRef.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == Roles.Condition) {
+ invocationExpression.ReplaceWith(arguments[0]);
+ return;
+ }
+
+ return;
+ }
+
+ static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name)
+ {
+ switch (name) {
+ case "op_Addition":
+ return BinaryOperatorType.Add;
+ case "op_Subtraction":
+ return BinaryOperatorType.Subtract;
+ case "op_Multiply":
+ return BinaryOperatorType.Multiply;
+ case "op_Division":
+ return BinaryOperatorType.Divide;
+ case "op_Modulus":
+ return BinaryOperatorType.Modulus;
+ case "op_BitwiseAnd":
+ return BinaryOperatorType.BitwiseAnd;
+ case "op_BitwiseOr":
+ return BinaryOperatorType.BitwiseOr;
+ case "op_ExclusiveOr":
+ return BinaryOperatorType.ExclusiveOr;
+ case "op_LeftShift":
+ return BinaryOperatorType.ShiftLeft;
+ case "op_RightShift":
+ return BinaryOperatorType.ShiftRight;
+ case "op_Equality":
+ return BinaryOperatorType.Equality;
+ case "op_Inequality":
+ return BinaryOperatorType.InEquality;
+ case "op_LessThan":
+ return BinaryOperatorType.LessThan;
+ case "op_LessThanOrEqual":
+ return BinaryOperatorType.LessThanOrEqual;
+ case "op_GreaterThan":
+ return BinaryOperatorType.GreaterThan;
+ case "op_GreaterThanOrEqual":
+ return BinaryOperatorType.GreaterThanOrEqual;
+ default:
+ return null;
+ }
+ }
+
+ static UnaryOperatorType? GetUnaryOperatorTypeFromMetadataName(string name)
+ {
+ switch (name) {
+ case "op_LogicalNot":
+ return UnaryOperatorType.Not;
+ case "op_OnesComplement":
+ return UnaryOperatorType.BitNot;
+ case "op_UnaryNegation":
+ return UnaryOperatorType.Minus;
+ case "op_UnaryPlus":
+ return UnaryOperatorType.Plus;
+ case "op_Increment":
+ return UnaryOperatorType.Increment;
+ case "op_Decrement":
+ return UnaryOperatorType.Decrement;
+ default:
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// This annotation is used to convert a compound assignment "a += 2;" or increment operator "a++;"
+ /// back to the original "a = a + 2;". This is sometimes necessary when the checked/unchecked semantics
+ /// cannot be guaranteed otherwise (see CheckedUnchecked.ForWithCheckedInitializerAndUncheckedIterator test)
+ /// </summary>
+ public class RestoreOriginalAssignOperatorAnnotation
+ {
+ readonly BinaryOperatorExpression binaryOperatorExpression;
+
+ public RestoreOriginalAssignOperatorAnnotation(BinaryOperatorExpression binaryOperatorExpression)
+ {
+ this.binaryOperatorExpression = binaryOperatorExpression;
+ }
+
+ public AssignmentExpression Restore(Expression expression)
+ {
+ expression.RemoveAnnotations<RestoreOriginalAssignOperatorAnnotation>();
+ AssignmentExpression assign = expression as AssignmentExpression;
+ if (assign == null) {
+ UnaryOperatorExpression uoe = (UnaryOperatorExpression)expression;
+ assign = new AssignmentExpression(uoe.Expression.Detach(), new PrimitiveExpression(1));
+ } else {
+ assign.Operator = AssignmentOperatorType.Assign;
+ }
+ binaryOperatorExpression.Right = assign.Right.Detach();
+ assign.Right = binaryOperatorExpression;
+ return assign;
+ }
+ }
+
+ public override object VisitAssignmentExpression(AssignmentExpression assignment, object data)
+ {
+ base.VisitAssignmentExpression(assignment, data);
+ // Combine "x = x op y" into "x op= y"
+ BinaryOperatorExpression binary = assignment.Right as BinaryOperatorExpression;
+ if (binary != null && assignment.Operator == AssignmentOperatorType.Assign) {
+ if (CanConvertToCompoundAssignment(assignment.Left) && assignment.Left.IsMatch(binary.Left)) {
+ assignment.Operator = GetAssignmentOperatorForBinaryOperator(binary.Operator);
+ if (assignment.Operator != AssignmentOperatorType.Assign) {
+ // If we found a shorter operator, get rid of the BinaryOperatorExpression:
+ assignment.CopyAnnotationsFrom(binary);
+ assignment.Right = binary.Right;
+ assignment.AddAnnotation(new RestoreOriginalAssignOperatorAnnotation(binary));
+ }
+ }
+ }
+ if (context.Settings.IntroduceIncrementAndDecrement && (assignment.Operator == AssignmentOperatorType.Add || assignment.Operator == AssignmentOperatorType.Subtract)) {
+ // detect increment/decrement
+ if (assignment.Right.IsMatch(new PrimitiveExpression(1))) {
+ // only if it's not a custom operator
+ if (assignment.Annotation<MethodReference>() == null) {
+ UnaryOperatorType type;
+ // When the parent is an expression statement, pre- or post-increment doesn't matter;
+ // so we can pick post-increment which is more commonly used (for (int i = 0; i < x; i++))
+ if (assignment.Parent is ExpressionStatement)
+ type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement;
+ else
+ type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.Increment : UnaryOperatorType.Decrement;
+ assignment.ReplaceWith(new UnaryOperatorExpression(type, assignment.Left.Detach()).CopyAnnotationsFrom(assignment));
+ }
+ }
+ }
+ return null;
+ }
+
+ public static AssignmentOperatorType GetAssignmentOperatorForBinaryOperator(BinaryOperatorType bop)
+ {
+ switch (bop) {
+ case BinaryOperatorType.Add:
+ return AssignmentOperatorType.Add;
+ case BinaryOperatorType.Subtract:
+ return AssignmentOperatorType.Subtract;
+ case BinaryOperatorType.Multiply:
+ return AssignmentOperatorType.Multiply;
+ case BinaryOperatorType.Divide:
+ return AssignmentOperatorType.Divide;
+ case BinaryOperatorType.Modulus:
+ return AssignmentOperatorType.Modulus;
+ case BinaryOperatorType.ShiftLeft:
+ return AssignmentOperatorType.ShiftLeft;
+ case BinaryOperatorType.ShiftRight:
+ return AssignmentOperatorType.ShiftRight;
+ case BinaryOperatorType.BitwiseAnd:
+ return AssignmentOperatorType.BitwiseAnd;
+ case BinaryOperatorType.BitwiseOr:
+ return AssignmentOperatorType.BitwiseOr;
+ case BinaryOperatorType.ExclusiveOr:
+ return AssignmentOperatorType.ExclusiveOr;
+ default:
+ return AssignmentOperatorType.Assign;
+ }
+ }
+
+ static bool CanConvertToCompoundAssignment(Expression left)
+ {
+ MemberReferenceExpression mre = left as MemberReferenceExpression;
+ if (mre != null)
+ return IsWithoutSideEffects(mre.Target);
+ IndexerExpression ie = left as IndexerExpression;
+ if (ie != null)
+ return IsWithoutSideEffects(ie.Target) && ie.Arguments.All(IsWithoutSideEffects);
+ UnaryOperatorExpression uoe = left as UnaryOperatorExpression;
+ if (uoe != null && uoe.Operator == UnaryOperatorType.Dereference)
+ return IsWithoutSideEffects(uoe.Expression);
+ return IsWithoutSideEffects(left);
+ }
+
+ static bool IsWithoutSideEffects(Expression left)
+ {
+ return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression || left is BaseReferenceExpression;
+ }
+
+ static readonly Expression getMethodOrConstructorFromHandlePattern =
+ new TypePattern(typeof(MethodBase)).ToType().Invoke(
+ "GetMethodFromHandle",
+ new NamedNode("ldtokenNode", new LdTokenPattern("method")).ToExpression().Member("MethodHandle"),
+ new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle"))
+ ).CastTo(new Choice {
+ new TypePattern(typeof(MethodInfo)),
+ new TypePattern(typeof(ConstructorInfo))
+ });
+
+ public override object VisitCastExpression(CastExpression castExpression, object data)
+ {
+ base.VisitCastExpression(castExpression, data);
+ // Handle methodof
+ Match m = getMethodOrConstructorFromHandlePattern.Match(castExpression);
+ if (m.Success) {
+ MethodReference method = m.Get<AstNode>("method").Single().Annotation<MethodReference>();
+ if (m.Has("declaringType")) {
+ Expression newNode = m.Get<AstType>("declaringType").Single().Detach().Member(method.Name);
+ newNode = newNode.Invoke(method.Parameters.Select(p => new TypeReferenceExpression(AstBuilder.ConvertType(p.ParameterType, p))));
+ newNode.AddAnnotation(method);
+ m.Get<AstNode>("method").Single().ReplaceWith(newNode);
+ }
+ castExpression.ReplaceWith(m.Get<AstNode>("ldtokenNode").Single());
+ }
+ return null;
+ }
+
+ void IAstTransform.Run(AstNode node)
+ {
+ node.AcceptVisitor(this, null);
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
new file mode 100644
index 00000000..3091a109
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
@@ -0,0 +1,65 @@
+// 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.Threading;
+using ICSharpCode.NRefactory.CSharp;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ public interface IAstTransform
+ {
+ void Run(AstNode compilationUnit);
+ }
+
+ public static class TransformationPipeline
+ {
+ public static IAstTransform[] CreatePipeline(DecompilerContext context)
+ {
+ return new IAstTransform[] {
+ new PushNegation(),
+ new DelegateConstruction(context),
+ new PatternStatementTransform(context),
+ new ReplaceMethodCallsWithOperators(context),
+ new IntroduceUnsafeModifier(),
+ new AddCheckedBlocks(),
+ new DeclareVariables(context), // should run after most transforms that modify statements
+ new ConvertConstructorCallIntoInitializer(), // must run after DeclareVariables
+ new DecimalConstantTransform(),
+ new IntroduceUsingDeclarations(context),
+ new IntroduceExtensionMethods(context), // must run after IntroduceUsingDeclarations
+ new IntroduceQueryExpressions(context), // must run after IntroduceExtensionMethods
+ new CombineQueryExpressions(context),
+ new FlattenSwitchBlocks(),
+ };
+ }
+
+ public static void RunTransformationsUntil(AstNode node, Predicate<IAstTransform> abortCondition, DecompilerContext context)
+ {
+ if (node == null)
+ return;
+
+ foreach (var transform in CreatePipeline(context)) {
+ context.CancellationToken.ThrowIfCancellationRequested();
+ if (abortCondition != null && abortCondition(transform))
+ return;
+ transform.Run(node);
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs b/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs
new file mode 100644
index 00000000..e4131904
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs
@@ -0,0 +1,528 @@
+// 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.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast
+{
+ public static class TypesHierarchyHelpers
+ {
+ public static bool IsBaseType(TypeDefinition baseType, TypeDefinition derivedType, bool resolveTypeArguments)
+ {
+ if (resolveTypeArguments)
+ return BaseTypes(derivedType).Any(t => t.Item == baseType);
+ else {
+ var comparableBaseType = baseType.Resolve();
+ if (comparableBaseType == null)
+ return false;
+ while (derivedType.BaseType != null) {
+ var resolvedBaseType = derivedType.BaseType.Resolve();
+ if (resolvedBaseType == null)
+ return false;
+ if (comparableBaseType == resolvedBaseType)
+ return true;
+ derivedType = resolvedBaseType;
+ }
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Determines whether one method overrides or hides another method.
+ /// </summary>
+ /// <param name="parentMethod">The method declared in a base type.</param>
+ /// <param name="childMethod">The method declared in a derived type.</param>
+ /// <returns>true if <paramref name="childMethod"/> hides or overrides <paramref name="parentMethod"/>,
+ /// otherwise false.</returns>
+ public static bool IsBaseMethod(MethodDefinition parentMethod, MethodDefinition childMethod)
+ {
+ if (parentMethod == null)
+ throw new ArgumentNullException("parentMethod");
+ if (childMethod == null)
+ throw new ArgumentNullException("childMethod");
+
+ if (parentMethod.Name != childMethod.Name)
+ return false;
+
+ if (parentMethod.HasParameters || childMethod.HasParameters)
+ if (!parentMethod.HasParameters || !childMethod.HasParameters || parentMethod.Parameters.Count != childMethod.Parameters.Count)
+ return false;
+
+ return FindBaseMethods(childMethod).Any(m => m == parentMethod);// || (parentMethod.HasGenericParameters && m.);
+ }
+
+ /// <summary>
+ /// Determines whether a property overrides or hides another property.
+ /// </summary>
+ /// <param name="parentProperty">The property declared in a base type.</param>
+ /// <param name="childProperty">The property declared in a derived type.</param>
+ /// <returns>true if the <paramref name="childProperty"/> hides or overrides <paramref name="parentProperty"/>,
+ /// otherwise false.</returns>
+ public static bool IsBaseProperty(PropertyDefinition parentProperty, PropertyDefinition childProperty)
+ {
+ if (parentProperty == null)
+ throw new ArgumentNullException("parentProperty");
+ if (childProperty == null)
+ throw new ArgumentNullException("childProperty");
+
+ if (parentProperty.Name != childProperty.Name)
+ return false;
+
+ if (parentProperty.HasParameters || childProperty.HasParameters)
+ if (!parentProperty.HasParameters || !childProperty.HasParameters || parentProperty.Parameters.Count != childProperty.Parameters.Count)
+ return false;
+
+ return FindBaseProperties(childProperty).Any(m => m == parentProperty);
+ }
+
+ public static bool IsBaseEvent(EventDefinition parentEvent, EventDefinition childEvent)
+ {
+ if (parentEvent.Name != childEvent.Name)
+ return false;
+
+ return FindBaseEvents(childEvent).Any(m => m == parentEvent);
+ }
+
+ /// <summary>
+ /// Finds all methods from base types overridden or hidden by the specified method.
+ /// </summary>
+ /// <param name="method">The method which overrides or hides methods from base types.</param>
+ /// <returns>Methods overriden or hidden by the specified method.</returns>
+ public static IEnumerable<MethodDefinition> FindBaseMethods(MethodDefinition method)
+ {
+ if (method == null)
+ throw new ArgumentNullException("method");
+
+ var typeContext = CreateGenericContext(method.DeclaringType);
+ var gMethod = typeContext.ApplyTo(method);
+
+ foreach (var baseType in BaseTypes(method.DeclaringType))
+ foreach (var baseMethod in baseType.Item.Methods)
+ if (MatchMethod(baseType.ApplyTo(baseMethod), gMethod) && IsVisibleFromDerived(baseMethod, method.DeclaringType)) {
+ yield return baseMethod;
+ if (baseMethod.IsNewSlot == baseMethod.IsVirtual)
+ yield break;
+ }
+ }
+
+ /// <summary>
+ /// Finds all properties from base types overridden or hidden by the specified property.
+ /// </summary>
+ /// <param name="property">The property which overrides or hides properties from base types.</param>
+ /// <returns>Properties overriden or hidden by the specified property.</returns>
+ public static IEnumerable<PropertyDefinition> FindBaseProperties(PropertyDefinition property)
+ {
+ if (property == null)
+ throw new ArgumentNullException("property");
+
+ if ((property.GetMethod ?? property.SetMethod).HasOverrides)
+ yield break;
+
+ var typeContext = CreateGenericContext(property.DeclaringType);
+ var gProperty = typeContext.ApplyTo(property);
+ bool isIndexer = property.IsIndexer();
+
+ foreach (var baseType in BaseTypes(property.DeclaringType))
+ foreach (var baseProperty in baseType.Item.Properties)
+ if (MatchProperty(baseType.ApplyTo(baseProperty), gProperty)
+ && IsVisibleFromDerived(baseProperty, property.DeclaringType)) {
+ if (isIndexer != baseProperty.IsIndexer())
+ continue;
+ yield return baseProperty;
+ var anyPropertyAccessor = baseProperty.GetMethod ?? baseProperty.SetMethod;
+ if (anyPropertyAccessor.IsNewSlot == anyPropertyAccessor.IsVirtual)
+ yield break;
+ }
+ }
+
+ public static IEnumerable<EventDefinition> FindBaseEvents(EventDefinition eventDef)
+ {
+ if (eventDef == null)
+ throw new ArgumentNullException("eventDef");
+
+ var typeContext = CreateGenericContext(eventDef.DeclaringType);
+ var gEvent = typeContext.ApplyTo(eventDef);
+
+ foreach (var baseType in BaseTypes(eventDef.DeclaringType))
+ foreach (var baseEvent in baseType.Item.Events)
+ if (MatchEvent(baseType.ApplyTo(baseEvent), gEvent) && IsVisibleFromDerived(baseEvent, eventDef.DeclaringType)) {
+ yield return baseEvent;
+ var anyEventAccessor = baseEvent.AddMethod ?? baseEvent.RemoveMethod;
+ if (anyEventAccessor.IsNewSlot == anyEventAccessor.IsVirtual)
+ yield break;
+ }
+
+ }
+
+ /// <summary>
+ /// Determinates whether member of the base type is visible from a derived type.
+ /// </summary>
+ /// <param name="baseMember">The member which visibility is checked.</param>
+ /// <param name="derivedType">The derived type.</param>
+ /// <returns>true if the member is visible from derived type, othewise false.</returns>
+ public static bool IsVisibleFromDerived(IMemberDefinition baseMember, TypeDefinition derivedType)
+ {
+ if (baseMember == null)
+ throw new ArgumentNullException("baseMember");
+ if (derivedType == null)
+ throw new ArgumentNullException("derivedType");
+
+ MethodAttributes attrs = GetAccessAttributes(baseMember) & MethodAttributes.MemberAccessMask;
+ if (attrs == MethodAttributes.Private)
+ return false;
+
+ if (baseMember.DeclaringType.Module == derivedType.Module)
+ return true;
+
+ if (attrs == MethodAttributes.Assembly || attrs == MethodAttributes.FamANDAssem) {
+ var derivedTypeAsm = derivedType.Module.Assembly;
+ var asm = baseMember.DeclaringType.Module.Assembly;
+
+ if (asm.HasCustomAttributes) {
+ var attributes = asm.CustomAttributes
+ .Where(attr => attr.AttributeType.FullName == "System.Runtime.CompilerServices.InternalsVisibleToAttribute");
+ foreach (var attribute in attributes) {
+ string assemblyName = attribute.ConstructorArguments[0].Value as string;
+ assemblyName = assemblyName.Split(',')[0]; // strip off any public key info
+ if (assemblyName == derivedTypeAsm.Name.Name)
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ static MethodAttributes GetAccessAttributes(IMemberDefinition member)
+ {
+ var fld = member as FieldDefinition;
+ if (fld != null)
+ return (MethodAttributes)fld.Attributes;
+
+ var method = member as MethodDefinition;
+ if (method != null)
+ return method.Attributes;
+
+ var prop = member as PropertyDefinition;
+ if (prop != null) {
+ return (prop.GetMethod ?? prop.SetMethod).Attributes;
+ }
+
+ var evnt = member as EventDefinition;
+ if (evnt != null) {
+ return (evnt.AddMethod ?? evnt.RemoveMethod).Attributes;
+ }
+
+ var nestedType = member as TypeDefinition;
+ if (nestedType != null) {
+ if (nestedType.IsNestedPrivate)
+ return MethodAttributes.Private;
+ if (nestedType.IsNestedAssembly || nestedType.IsNestedFamilyAndAssembly)
+ return MethodAttributes.Assembly;
+ return MethodAttributes.Public;
+ }
+
+ throw new NotSupportedException();
+ }
+
+ static bool MatchMethod(GenericContext<MethodDefinition> candidate, GenericContext<MethodDefinition> method)
+ {
+ var mCandidate = candidate.Item;
+ var mMethod = method.Item;
+ if (mCandidate.Name != mMethod.Name)
+ return false;
+
+ if (mCandidate.HasOverrides)
+ return false;
+
+ if (mCandidate.IsSpecialName != method.Item.IsSpecialName)
+ return false;
+
+ if (mCandidate.HasGenericParameters || mMethod.HasGenericParameters) {
+ if (!mCandidate.HasGenericParameters || !mMethod.HasGenericParameters || mCandidate.GenericParameters.Count != mMethod.GenericParameters.Count)
+ return false;
+ }
+
+ if (mCandidate.HasParameters || mMethod.HasParameters) {
+ if (!mCandidate.HasParameters || !mMethod.HasParameters || mCandidate.Parameters.Count != mMethod.Parameters.Count)
+ return false;
+
+ for (int index = 0; index < mCandidate.Parameters.Count; index++) {
+ if (!MatchParameters(candidate.ApplyTo(mCandidate.Parameters[index]), method.ApplyTo(mMethod.Parameters[index])))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static bool MatchInterfaceMethod(MethodDefinition candidate, MethodDefinition method, TypeReference interfaceContextType)
+ {
+ var candidateContext = CreateGenericContext(candidate.DeclaringType);
+ var gCandidate = candidateContext.ApplyTo(candidate);
+
+ if (interfaceContextType is GenericInstanceType) {
+ var methodContext = new GenericContext<TypeDefinition>(interfaceContextType.Resolve(), ((GenericInstanceType)interfaceContextType).GenericArguments);
+ var gMethod = methodContext.ApplyTo(method);
+ return MatchMethod(gCandidate, gMethod);
+ } else {
+ var methodContext = CreateGenericContext(interfaceContextType.Resolve());
+ var gMethod = candidateContext.ApplyTo(method);
+ return MatchMethod(gCandidate, gMethod);
+ }
+ }
+
+ static bool MatchProperty(GenericContext<PropertyDefinition> candidate, GenericContext<PropertyDefinition> property)
+ {
+ var mCandidate = candidate.Item;
+ var mProperty = property.Item;
+ if (mCandidate.Name != mProperty.Name)
+ return false;
+
+ if ((mCandidate.GetMethod ?? mCandidate.SetMethod).HasOverrides)
+ return false;
+
+ if (mCandidate.HasParameters || mProperty.HasParameters) {
+ if (!mCandidate.HasParameters || !mProperty.HasParameters || mCandidate.Parameters.Count != mProperty.Parameters.Count)
+ return false;
+
+ for (int index = 0; index < mCandidate.Parameters.Count; index++) {
+ if (!MatchParameters(candidate.ApplyTo(mCandidate.Parameters[index]), property.ApplyTo(mProperty.Parameters[index])))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ static bool MatchEvent(GenericContext<EventDefinition> candidate, GenericContext<EventDefinition> ev)
+ {
+ var mCandidate = candidate.Item;
+ var mEvent = ev.Item;
+ if (mCandidate.Name != mEvent.Name)
+ return false;
+
+ if ((mCandidate.AddMethod ?? mCandidate.RemoveMethod).HasOverrides)
+ return false;
+
+ if (!IsSameType(candidate.ResolveWithContext(mCandidate.EventType), ev.ResolveWithContext(mEvent.EventType)))
+ return false;
+
+ return true;
+ }
+
+ static bool MatchParameters(GenericContext<ParameterDefinition> baseParameterType, GenericContext<ParameterDefinition> parameterType)
+ {
+ if (baseParameterType.Item.IsIn != parameterType.Item.IsIn ||
+ baseParameterType.Item.IsOut != parameterType.Item.IsOut)
+ return false;
+ var baseParam = baseParameterType.ResolveWithContext(baseParameterType.Item.ParameterType);
+ var param = parameterType.ResolveWithContext(parameterType.Item.ParameterType);
+ return IsSameType(baseParam, param);
+ }
+
+ static bool IsSameType(TypeReference tr1, TypeReference tr2)
+ {
+ if (tr1 == tr2)
+ return true;
+ if (tr1 == null || tr2 == null)
+ return false;
+
+ if (tr1.GetType() != tr2.GetType())
+ return false;
+
+ if (tr1.Name == tr2.Name && tr1.FullName == tr2.FullName)
+ return true;
+
+ return false;
+ }
+
+ static IEnumerable<GenericContext<TypeDefinition>> BaseTypes(TypeDefinition type)
+ {
+ return BaseTypes(CreateGenericContext(type));
+ }
+
+ static IEnumerable<GenericContext<TypeDefinition>> BaseTypes(GenericContext<TypeDefinition> type)
+ {
+ while (type.Item.BaseType != null) {
+ var baseType = type.Item.BaseType;
+ var genericBaseType = baseType as GenericInstanceType;
+ if (genericBaseType != null) {
+ type = new GenericContext<TypeDefinition>(genericBaseType.ResolveOrThrow(),
+ genericBaseType.GenericArguments.Select(t => type.ResolveWithContext(t)));
+ } else
+ type = new GenericContext<TypeDefinition>(baseType.ResolveOrThrow());
+ yield return type;
+ }
+ }
+
+ static GenericContext<TypeDefinition> CreateGenericContext(TypeDefinition type)
+ {
+ return type.HasGenericParameters
+ ? new GenericContext<TypeDefinition>(type, type.GenericParameters)
+ : new GenericContext<TypeDefinition>(type);
+ }
+
+ struct GenericContext<T> where T : class
+ {
+ static readonly ReadOnlyCollection<TypeReference> Empty = new ReadOnlyCollection<TypeReference>(new List<TypeReference>());
+
+ static readonly GenericParameter UnresolvedGenericTypeParameter =
+ new DummyGenericParameterProvider(false).DummyParameter;
+
+ static readonly GenericParameter UnresolvedGenericMethodParameter =
+ new DummyGenericParameterProvider(true).DummyParameter;
+
+ public readonly T Item;
+ public readonly ReadOnlyCollection<TypeReference> TypeArguments;
+
+ public GenericContext(T item)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+
+ Item = item;
+ TypeArguments = Empty;
+ }
+
+ public GenericContext(T item, IEnumerable<TypeReference> typeArguments)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+
+ Item = item;
+ var list = new List<TypeReference>();
+ foreach (var arg in typeArguments) {
+ var resolved = arg != null ? arg.Resolve() : arg;
+ list.Add(resolved != null ? resolved : arg);
+ }
+ TypeArguments = new ReadOnlyCollection<TypeReference>(list);
+ }
+
+ GenericContext(T item, ReadOnlyCollection<TypeReference> typeArguments)
+ {
+ Item = item;
+ TypeArguments = typeArguments;
+ }
+
+ public TypeReference ResolveWithContext(TypeReference type)
+ {
+ var genericParameter = type as GenericParameter;
+ if (genericParameter != null)
+ if (genericParameter.Owner.GenericParameterType == GenericParameterType.Type)
+ return TypeArguments[genericParameter.Position];
+ else
+ return genericParameter.Owner.GenericParameterType == GenericParameterType.Type
+ ? UnresolvedGenericTypeParameter : UnresolvedGenericMethodParameter;
+ var typeSpecification = type as TypeSpecification;
+ if (typeSpecification != null) {
+ var resolvedElementType = ResolveWithContext(typeSpecification.ElementType);
+ return ReplaceElementType(typeSpecification, resolvedElementType);
+ }
+ return type.ResolveOrThrow();
+ }
+
+ TypeReference ReplaceElementType(TypeSpecification ts, TypeReference newElementType)
+ {
+ var arrayType = ts as ArrayType;
+ if (arrayType != null) {
+ if (newElementType == arrayType.ElementType)
+ return arrayType;
+ var newArrayType = new ArrayType(newElementType, arrayType.Rank);
+ for (int dimension = 0; dimension < arrayType.Rank; dimension++)
+ newArrayType.Dimensions[dimension] = arrayType.Dimensions[dimension];
+ return newArrayType;
+ }
+ var byReferenceType = ts as ByReferenceType;
+ if (byReferenceType != null) {
+ return new ByReferenceType(newElementType);
+ }
+ // TODO: should we throw an exception instead calling Resolve method?
+ return ts.ResolveOrThrow();
+ }
+
+ public GenericContext<T2> ApplyTo<T2>(T2 item) where T2 : class
+ {
+ return new GenericContext<T2>(item, TypeArguments);
+ }
+
+ class DummyGenericParameterProvider : IGenericParameterProvider
+ {
+ readonly GenericParameterType type;
+ readonly Mono.Collections.Generic.Collection<GenericParameter> parameters;
+
+ public DummyGenericParameterProvider(bool methodTypeParameter)
+ {
+ type = methodTypeParameter ? GenericParameterType.Method :
+ GenericParameterType.Type;
+ parameters = new Mono.Collections.Generic.Collection<GenericParameter>(1);
+ parameters.Add(new GenericParameter(this));
+ }
+
+ public GenericParameter DummyParameter
+ {
+ get { return parameters[0]; }
+ }
+
+ bool IGenericParameterProvider.HasGenericParameters
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ bool IGenericParameterProvider.IsDefinition
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ ModuleDefinition IGenericParameterProvider.Module
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ Mono.Collections.Generic.Collection<GenericParameter> IGenericParameterProvider.GenericParameters
+ {
+ get { return parameters; }
+ }
+
+ GenericParameterType IGenericParameterProvider.GenericParameterType
+ {
+ get { return type; }
+ }
+
+ MetadataToken IMetadataTokenProvider.MetadataToken
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ set
+ {
+ throw new NotImplementedException();
+ }
+ }
+ }
+ }
+ }
+}