summaryrefslogtreecommitdiff
path: root/ICSharpCode.Decompiler
diff options
context:
space:
mode:
Diffstat (limited to 'ICSharpCode.Decompiler')
-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
-rw-r--r--ICSharpCode.Decompiler/CecilExtensions.cs374
-rw-r--r--ICSharpCode.Decompiler/CodeMappings.cs57
-rw-r--r--ICSharpCode.Decompiler/DecompilerException.cs42
-rw-r--r--ICSharpCode.Decompiler/DecompilerSettings.cs356
-rw-r--r--ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs447
-rw-r--r--ICSharpCode.Decompiler/Disassembler/ILStructure.cs228
-rw-r--r--ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs239
-rw-r--r--ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs1168
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs78
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs191
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs439
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs305
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs241
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs312
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs174
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs60
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs162
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs257
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/SsaInstruction.cs191
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs138
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs91
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs254
-rw-r--r--ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj159
-rw-r--r--ICSharpCode.Decompiler/ICSharpCode.Decompiler.sln17
-rw-r--r--ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs704
-rw-r--r--ICSharpCode.Decompiler/ILAst/DefaultDictionary.cs128
-rw-r--r--ICSharpCode.Decompiler/ILAst/GotoRemoval.cs319
-rw-r--r--ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs839
-rw-r--r--ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs988
-rw-r--r--ICSharpCode.Decompiler/ILAst/ILAstTypes.cs601
-rw-r--r--ICSharpCode.Decompiler/ILAst/ILCodes.cs490
-rw-r--r--ICSharpCode.Decompiler/ILAst/ILInlining.cs524
-rw-r--r--ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs543
-rw-r--r--ICSharpCode.Decompiler/ILAst/LiftedOperators.cs528
-rw-r--r--ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs443
-rw-r--r--ICSharpCode.Decompiler/ILAst/PatternMatching.cs177
-rw-r--r--ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs1103
-rw-r--r--ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs376
-rw-r--r--ICSharpCode.Decompiler/ILAst/StateRange.cs312
-rw-r--r--ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs157
-rw-r--r--ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs1294
-rw-r--r--ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs635
-rw-r--r--ICSharpCode.Decompiler/ITextOutput.cs62
-rw-r--r--ICSharpCode.Decompiler/PlainTextOutput.cs122
-rw-r--r--ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs27
-rw-r--r--ICSharpCode.Decompiler/ReferenceResolvingException.cs68
-rw-r--r--ICSharpCode.Decompiler/Tests/Async.cs155
-rw-r--r--ICSharpCode.Decompiler/Tests/BooleanConsumedAsInteger.il59
-rw-r--r--ICSharpCode.Decompiler/Tests/CallOverloadedMethod.cs52
-rw-r--r--ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs117
-rw-r--r--ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs133
-rw-r--r--ICSharpCode.Decompiler/Tests/ControlFlow.cs97
-rw-r--r--ICSharpCode.Decompiler/Tests/CustomAttributes.code.cs41
-rw-r--r--ICSharpCode.Decompiler/Tests/CustomAttributes/CustomAttributeTests.cs30
-rw-r--r--ICSharpCode.Decompiler/Tests/CustomAttributes/S_AssemblyCustomAttribute.cs21
-rw-r--r--ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs508
-rw-r--r--ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs79
-rw-r--r--ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs88
-rw-r--r--ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs89
-rw-r--r--ICSharpCode.Decompiler/Tests/DelegateConstruction.cs205
-rw-r--r--ICSharpCode.Decompiler/Tests/DoubleConstants.cs28
-rw-r--r--ICSharpCode.Decompiler/Tests/ExceptionHandling.cs128
-rw-r--r--ICSharpCode.Decompiler/Tests/ExpressionTrees.cs370
-rw-r--r--ICSharpCode.Decompiler/Tests/Generics.cs165
-rw-r--r--ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs110
-rw-r--r--ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs38
-rw-r--r--ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj137
-rw-r--r--ICSharpCode.Decompiler/Tests/IL/ILTests.cs51
-rw-r--r--ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.Output.cs53
-rw-r--r--ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.il140
-rw-r--r--ICSharpCode.Decompiler/Tests/IL/StackTests.il132
-rw-r--r--ICSharpCode.Decompiler/Tests/IncrementDecrement.cs254
-rw-r--r--ICSharpCode.Decompiler/Tests/InitializerTests.cs885
-rw-r--r--ICSharpCode.Decompiler/Tests/LiftedOperators.cs830
-rw-r--r--ICSharpCode.Decompiler/Tests/Lock.cs38
-rw-r--r--ICSharpCode.Decompiler/Tests/Loops.cs74
-rw-r--r--ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs58
-rw-r--r--ICSharpCode.Decompiler/Tests/PInvoke.cs96
-rw-r--r--ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs80
-rw-r--r--ICSharpCode.Decompiler/Tests/QueryExpressions.cs188
-rw-r--r--ICSharpCode.Decompiler/Tests/Switch.cs89
-rw-r--r--ICSharpCode.Decompiler/Tests/TestRunner.cs198
-rw-r--r--ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs153
-rw-r--r--ICSharpCode.Decompiler/Tests/Types/EnumTests.cs18
-rw-r--r--ICSharpCode.Decompiler/Tests/Types/S_EnumSamples.cs129
-rw-r--r--ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs17
-rw-r--r--ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs1138
-rw-r--r--ICSharpCode.Decompiler/Tests/Types/TypeTests.cs18
-rw-r--r--ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs41
-rw-r--r--ICSharpCode.Decompiler/Tests/UnsafeCode.cs145
-rw-r--r--ICSharpCode.Decompiler/Tests/ValueTypes.cs188
-rw-r--r--ICSharpCode.Decompiler/Tests/YieldReturn.cs148
-rw-r--r--ICSharpCode.Decompiler/Tests/packages.config6
-rw-r--r--ICSharpCode.Decompiler/TextOutputWriter.cs55
-rw-r--r--ICSharpCode.Decompiler/packages.config5
122 files changed, 34043 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();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/CecilExtensions.cs b/ICSharpCode.Decompiler/CecilExtensions.cs
new file mode 100644
index 00000000..0c7cb1e4
--- /dev/null
+++ b/ICSharpCode.Decompiler/CecilExtensions.cs
@@ -0,0 +1,374 @@
+// 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 Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler
+{
+ /// <summary>
+ /// Cecil helper methods.
+ /// </summary>
+ public static class CecilExtensions
+ {
+ #region GetPushDelta / GetPopDelta
+ public static int GetPushDelta(this Instruction instruction)
+ {
+ OpCode code = instruction.OpCode;
+ switch (code.StackBehaviourPush) {
+ case StackBehaviour.Push0:
+ return 0;
+
+ case StackBehaviour.Push1:
+ case StackBehaviour.Pushi:
+ case StackBehaviour.Pushi8:
+ case StackBehaviour.Pushr4:
+ case StackBehaviour.Pushr8:
+ case StackBehaviour.Pushref:
+ return 1;
+
+ case StackBehaviour.Push1_push1:
+ return 2;
+
+ case StackBehaviour.Varpush:
+ if (code.FlowControl != FlowControl.Call)
+ break;
+
+ IMethodSignature method = (IMethodSignature) instruction.Operand;
+ return IsVoid (method.ReturnType) ? 0 : 1;
+ }
+
+ throw new NotSupportedException ();
+ }
+
+ public static int? GetPopDelta(this Instruction instruction, MethodDefinition methodDef)
+ {
+ OpCode code = instruction.OpCode;
+ switch (code.StackBehaviourPop) {
+ case StackBehaviour.Pop0:
+ return 0;
+ case StackBehaviour.Popi:
+ case StackBehaviour.Popref:
+ case StackBehaviour.Pop1:
+ return 1;
+
+ case StackBehaviour.Pop1_pop1:
+ case StackBehaviour.Popi_pop1:
+ case StackBehaviour.Popi_popi:
+ case StackBehaviour.Popi_popi8:
+ case StackBehaviour.Popi_popr4:
+ case StackBehaviour.Popi_popr8:
+ case StackBehaviour.Popref_pop1:
+ case StackBehaviour.Popref_popi:
+ return 2;
+
+ case StackBehaviour.Popi_popi_popi:
+ case StackBehaviour.Popref_popi_popi:
+ case StackBehaviour.Popref_popi_popi8:
+ case StackBehaviour.Popref_popi_popr4:
+ case StackBehaviour.Popref_popi_popr8:
+ case StackBehaviour.Popref_popi_popref:
+ return 3;
+
+ case StackBehaviour.PopAll:
+ return null;
+
+ case StackBehaviour.Varpop:
+ if (code == OpCodes.Ret)
+ return methodDef.ReturnType.IsVoid() ? 0 : 1;
+
+ if (code.FlowControl != FlowControl.Call)
+ break;
+
+ IMethodSignature method = (IMethodSignature) instruction.Operand;
+ int count = method.HasParameters ? method.Parameters.Count : 0;
+ if (method.HasThis && code != OpCodes.Newobj)
+ ++count;
+ if (code == OpCodes.Calli)
+ ++count; // calli takes a function pointer in additional to the normal args
+
+ return count;
+ }
+
+ throw new NotSupportedException ();
+ }
+
+ public static bool IsVoid(this TypeReference type)
+ {
+ while (type is OptionalModifierType || type is RequiredModifierType)
+ type = ((TypeSpecification)type).ElementType;
+ return type.MetadataType == MetadataType.Void;
+ }
+
+ public static bool IsValueTypeOrVoid(this TypeReference type)
+ {
+ while (type is OptionalModifierType || type is RequiredModifierType)
+ type = ((TypeSpecification)type).ElementType;
+ if (type is ArrayType)
+ return false;
+ return type.IsValueType || type.IsVoid();
+ }
+
+ /// <summary>
+ /// checks if the given TypeReference is one of the following types:
+ /// [sbyte, short, int, long, IntPtr]
+ /// </summary>
+ public static bool IsSignedIntegralType(this TypeReference type)
+ {
+ return type.MetadataType == MetadataType.SByte ||
+ type.MetadataType == MetadataType.Int16 ||
+ type.MetadataType == MetadataType.Int32 ||
+ type.MetadataType == MetadataType.Int64 ||
+ type.MetadataType == MetadataType.IntPtr;
+ }
+
+ /// <summary>
+ /// checks if the given value is a numeric zero-value.
+ /// NOTE that this only works for types: [sbyte, short, int, long, IntPtr, byte, ushort, uint, ulong, float, double and decimal]
+ /// </summary>
+ public static bool IsZero(this object value)
+ {
+ return value.Equals((sbyte)0) ||
+ value.Equals((short)0) ||
+ value.Equals(0) ||
+ value.Equals(0L) ||
+ value.Equals(IntPtr.Zero) ||
+ value.Equals((byte)0) ||
+ value.Equals((ushort)0) ||
+ value.Equals(0u) ||
+ value.Equals(0UL) ||
+ value.Equals(0.0f) ||
+ value.Equals(0.0) ||
+ value.Equals((decimal)0);
+
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the (exclusive) end offset of this instruction.
+ /// </summary>
+ public static int GetEndOffset(this Instruction inst)
+ {
+ if (inst == null)
+ throw new ArgumentNullException("inst");
+ return inst.Offset + inst.GetSize();
+ }
+
+ public static string OffsetToString(int offset)
+ {
+ return string.Format("IL_{0:x4}", offset);
+ }
+
+ public static HashSet<MethodDefinition> GetAccessorMethods(this TypeDefinition type)
+ {
+ HashSet<MethodDefinition> accessorMethods = new HashSet<MethodDefinition>();
+ foreach (var property in type.Properties) {
+ accessorMethods.Add(property.GetMethod);
+ accessorMethods.Add(property.SetMethod);
+ if (property.HasOtherMethods) {
+ foreach (var m in property.OtherMethods)
+ accessorMethods.Add(m);
+ }
+ }
+ foreach (EventDefinition ev in type.Events) {
+ accessorMethods.Add(ev.AddMethod);
+ accessorMethods.Add(ev.RemoveMethod);
+ accessorMethods.Add(ev.InvokeMethod);
+ if (ev.HasOtherMethods) {
+ foreach (var m in ev.OtherMethods)
+ accessorMethods.Add(m);
+ }
+ }
+ return accessorMethods;
+ }
+
+ public static TypeDefinition ResolveWithinSameModule(this TypeReference type)
+ {
+ if (type != null && type.GetElementType().Module == type.Module)
+ return type.Resolve();
+ else
+ return null;
+ }
+
+ public static FieldDefinition ResolveWithinSameModule(this FieldReference field)
+ {
+ if (field != null && field.DeclaringType.GetElementType().Module == field.Module)
+ return field.Resolve();
+ else
+ return null;
+ }
+
+ public static MethodDefinition ResolveWithinSameModule(this MethodReference method)
+ {
+ if (method != null && method.DeclaringType.GetElementType().Module == method.Module)
+ return method.Resolve();
+ else
+ return null;
+ }
+
+ [Obsolete("throwing exceptions is considered a bug")]
+ public static TypeDefinition ResolveOrThrow(this TypeReference typeReference)
+ {
+ var resolved = typeReference.Resolve();
+ if (resolved == null)
+ throw new ReferenceResolvingException();
+ return resolved;
+ }
+
+ public static bool IsCompilerGenerated(this ICustomAttributeProvider provider)
+ {
+ if (provider != null && provider.HasCustomAttributes) {
+ foreach (CustomAttribute a in provider.CustomAttributes) {
+ if (a.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute")
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static bool IsCompilerGeneratedOrIsInCompilerGeneratedClass(this IMemberDefinition member)
+ {
+ if (member == null)
+ return false;
+ if (member.IsCompilerGenerated())
+ return true;
+ return IsCompilerGeneratedOrIsInCompilerGeneratedClass(member.DeclaringType);
+ }
+
+ public static TypeReference GetEnumUnderlyingType(this TypeDefinition type)
+ {
+ if (!type.IsEnum)
+ throw new ArgumentException("Type must be an enum", "type");
+
+ var fields = type.Fields;
+
+ for (int i = 0; i < fields.Count; i++)
+ {
+ var field = fields[i];
+ if (!field.IsStatic)
+ return field.FieldType;
+ }
+
+ throw new NotSupportedException();
+ }
+
+ public static bool IsAnonymousType(this TypeReference type)
+ {
+ if (type == null)
+ return false;
+ if (string.IsNullOrEmpty(type.Namespace) && type.HasGeneratedName() && (type.Name.Contains("AnonType") || type.Name.Contains("AnonymousType"))) {
+ TypeDefinition td = type.Resolve();
+ return td != null && td.IsCompilerGenerated();
+ }
+ return false;
+ }
+
+ public static bool HasGeneratedName(this MemberReference member)
+ {
+ return member.Name.StartsWith("<", StringComparison.Ordinal);
+ }
+
+ public static bool ContainsAnonymousType(this TypeReference type)
+ {
+ GenericInstanceType git = type as GenericInstanceType;
+ if (git != null) {
+ if (IsAnonymousType(git))
+ return true;
+ for (int i = 0; i < git.GenericArguments.Count; i++) {
+ if (git.GenericArguments[i].ContainsAnonymousType())
+ return true;
+ }
+ return false;
+ }
+ TypeSpecification typeSpec = type as TypeSpecification;
+ if (typeSpec != null)
+ return typeSpec.ElementType.ContainsAnonymousType();
+ else
+ return false;
+ }
+
+ public static string GetDefaultMemberName(this TypeDefinition type)
+ {
+ CustomAttribute attr;
+ return type.GetDefaultMemberName(out attr);
+ }
+
+ public static string GetDefaultMemberName(this TypeDefinition type, out CustomAttribute defaultMemberAttribute)
+ {
+ if (type.HasCustomAttributes)
+ foreach (CustomAttribute ca in type.CustomAttributes)
+ if (ca.Constructor.DeclaringType.Name == "DefaultMemberAttribute" && ca.Constructor.DeclaringType.Namespace == "System.Reflection"
+ && ca.Constructor.FullName == @"System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") {
+ defaultMemberAttribute = ca;
+ return ca.ConstructorArguments[0].Value as string;
+ }
+ defaultMemberAttribute = null;
+ return null;
+ }
+
+ public static bool IsIndexer(this PropertyDefinition property)
+ {
+ CustomAttribute attr;
+ return property.IsIndexer(out attr);
+ }
+
+ public static bool IsIndexer(this PropertyDefinition property, out CustomAttribute defaultMemberAttribute)
+ {
+ defaultMemberAttribute = null;
+ if (property.HasParameters) {
+ var accessor = property.GetMethod ?? property.SetMethod;
+ PropertyDefinition basePropDef = property;
+ if (accessor.HasOverrides) {
+ // if the property is explicitly implementing an interface, look up the property in the interface:
+ MethodDefinition baseAccessor = accessor.Overrides.First().Resolve();
+ if (baseAccessor != null) {
+ foreach (PropertyDefinition baseProp in baseAccessor.DeclaringType.Properties) {
+ if (baseProp.GetMethod == baseAccessor || baseProp.SetMethod == baseAccessor) {
+ basePropDef = baseProp;
+ break;
+ }
+ }
+ } else
+ return false;
+ }
+ CustomAttribute attr;
+ var defaultMemberName = basePropDef.DeclaringType.GetDefaultMemberName(out attr);
+ if (defaultMemberName == basePropDef.Name) {
+ defaultMemberAttribute = attr;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static bool IsDelegate(this TypeDefinition type)
+ {
+ if (type.BaseType != null && type.BaseType.Namespace == "System") {
+ if (type.BaseType.Name == "MulticastDelegate")
+ return true;
+ if (type.BaseType.Name == "Delegate" && type.Name != "MulticastDelegate")
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/CodeMappings.cs b/ICSharpCode.Decompiler/CodeMappings.cs
new file mode 100644
index 00000000..ee62f76c
--- /dev/null
+++ b/ICSharpCode.Decompiler/CodeMappings.cs
@@ -0,0 +1,57 @@
+// 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 ICSharpCode.NRefactory;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler
+{
+ /// <summary> Maps method's source code to IL </summary>
+ public class MethodDebugSymbols
+ {
+ public MethodDefinition CecilMethod { get; set; }
+ public List<ILVariable> LocalVariables { get; set; }
+ public List<SequencePoint> SequencePoints { get; set; }
+ public TextLocation StartLocation { get; set; }
+ public TextLocation EndLocation { get; set; }
+
+ public MethodDebugSymbols(MethodDefinition methodDef)
+ {
+ CecilMethod = methodDef;
+ LocalVariables = new List<ILVariable>();
+ SequencePoints = new List<SequencePoint>();
+ }
+ }
+
+ public class SequencePoint
+ {
+ public ILRange[] ILRanges { get; set; }
+ public TextLocation StartLocation { get; set; }
+ public TextLocation EndLocation { get; set; }
+ public int ILOffset { get { return ILRanges[0].From; } }
+
+ public override string ToString()
+ {
+ return string.Join(" ", ILRanges) + " " + StartLocation + "-" + EndLocation;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/DecompilerException.cs b/ICSharpCode.Decompiler/DecompilerException.cs
new file mode 100644
index 00000000..5f5f022f
--- /dev/null
+++ b/ICSharpCode.Decompiler/DecompilerException.cs
@@ -0,0 +1,42 @@
+// 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.Runtime.Serialization;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler
+{
+ /// <summary>
+ /// Description of DecompilerException.
+ /// </summary>
+ public class DecompilerException : Exception, ISerializable
+ {
+ public MethodDefinition DecompiledMethod { get; set; }
+
+ public DecompilerException(MethodDefinition decompiledMethod, Exception innerException)
+ : base("Error decompiling " + decompiledMethod.FullName + Environment.NewLine, innerException)
+ {
+ }
+
+ // This constructor is needed for serialization.
+ protected DecompilerException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs
new file mode 100644
index 00000000..74bdc943
--- /dev/null
+++ b/ICSharpCode.Decompiler/DecompilerSettings.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.ComponentModel;
+using ICSharpCode.NRefactory.CSharp;
+
+namespace ICSharpCode.Decompiler
+{
+ /// <summary>
+ /// Settings for the decompiler.
+ /// </summary>
+ public class DecompilerSettings : INotifyPropertyChanged
+ {
+ bool anonymousMethods = true;
+
+ /// <summary>
+ /// Decompile anonymous methods/lambdas.
+ /// </summary>
+ public bool AnonymousMethods {
+ get { return anonymousMethods; }
+ set {
+ if (anonymousMethods != value) {
+ anonymousMethods = value;
+ OnPropertyChanged("AnonymousMethods");
+ }
+ }
+ }
+
+ bool expressionTrees = true;
+
+ /// <summary>
+ /// Decompile expression trees.
+ /// </summary>
+ public bool ExpressionTrees {
+ get { return expressionTrees; }
+ set {
+ if (expressionTrees != value) {
+ expressionTrees = value;
+ OnPropertyChanged("ExpressionTrees");
+ }
+ }
+ }
+
+ bool yieldReturn = true;
+
+ /// <summary>
+ /// Decompile enumerators.
+ /// </summary>
+ public bool YieldReturn {
+ get { return yieldReturn; }
+ set {
+ if (yieldReturn != value) {
+ yieldReturn = value;
+ OnPropertyChanged("YieldReturn");
+ }
+ }
+ }
+
+ bool asyncAwait = true;
+
+ /// <summary>
+ /// Decompile async methods.
+ /// </summary>
+ public bool AsyncAwait {
+ get { return asyncAwait; }
+ set {
+ if (asyncAwait != value) {
+ asyncAwait = value;
+ OnPropertyChanged("AsyncAwait");
+ }
+ }
+ }
+
+ bool automaticProperties = true;
+
+ /// <summary>
+ /// Decompile automatic properties
+ /// </summary>
+ public bool AutomaticProperties {
+ get { return automaticProperties; }
+ set {
+ if (automaticProperties != value) {
+ automaticProperties = value;
+ OnPropertyChanged("AutomaticProperties");
+ }
+ }
+ }
+
+ bool automaticEvents = true;
+
+ /// <summary>
+ /// Decompile automatic events
+ /// </summary>
+ public bool AutomaticEvents {
+ get { return automaticEvents; }
+ set {
+ if (automaticEvents != value) {
+ automaticEvents = value;
+ OnPropertyChanged("AutomaticEvents");
+ }
+ }
+ }
+
+ bool usingStatement = true;
+
+ /// <summary>
+ /// Decompile using statements.
+ /// </summary>
+ public bool UsingStatement {
+ get { return usingStatement; }
+ set {
+ if (usingStatement != value) {
+ usingStatement = value;
+ OnPropertyChanged("UsingStatement");
+ }
+ }
+ }
+
+ bool forEachStatement = true;
+
+ /// <summary>
+ /// Decompile foreach statements.
+ /// </summary>
+ public bool ForEachStatement {
+ get { return forEachStatement; }
+ set {
+ if (forEachStatement != value) {
+ forEachStatement = value;
+ OnPropertyChanged("ForEachStatement");
+ }
+ }
+ }
+
+ bool lockStatement = true;
+
+ /// <summary>
+ /// Decompile lock statements.
+ /// </summary>
+ public bool LockStatement {
+ get { return lockStatement; }
+ set {
+ if (lockStatement != value) {
+ lockStatement = value;
+ OnPropertyChanged("LockStatement");
+ }
+ }
+ }
+
+ bool switchStatementOnString = true;
+
+ public bool SwitchStatementOnString {
+ get { return switchStatementOnString; }
+ set {
+ if (switchStatementOnString != value) {
+ switchStatementOnString = value;
+ OnPropertyChanged("SwitchStatementOnString");
+ }
+ }
+ }
+
+ bool usingDeclarations = true;
+
+ public bool UsingDeclarations {
+ get { return usingDeclarations; }
+ set {
+ if (usingDeclarations != value) {
+ usingDeclarations = value;
+ OnPropertyChanged("UsingDeclarations");
+ }
+ }
+ }
+
+ bool queryExpressions = true;
+
+ public bool QueryExpressions {
+ get { return queryExpressions; }
+ set {
+ if (queryExpressions != value) {
+ queryExpressions = value;
+ OnPropertyChanged("QueryExpressions");
+ }
+ }
+ }
+
+ bool fullyQualifyAmbiguousTypeNames = true;
+
+ public bool FullyQualifyAmbiguousTypeNames {
+ get { return fullyQualifyAmbiguousTypeNames; }
+ set {
+ if (fullyQualifyAmbiguousTypeNames != value) {
+ fullyQualifyAmbiguousTypeNames = value;
+ OnPropertyChanged("FullyQualifyAmbiguousTypeNames");
+ }
+ }
+ }
+
+ bool useDebugSymbols = true;
+
+ /// <summary>
+ /// Gets/Sets whether to use variable names from debug symbols, if available.
+ /// </summary>
+ public bool UseDebugSymbols {
+ get { return useDebugSymbols; }
+ set {
+ if (useDebugSymbols != value) {
+ useDebugSymbols = value;
+ OnPropertyChanged("UseDebugSymbols");
+ }
+ }
+ }
+
+ bool objectCollectionInitializers = true;
+
+ /// <summary>
+ /// Gets/Sets whether to use C# 3.0 object/collection initializers
+ /// </summary>
+ public bool ObjectOrCollectionInitializers {
+ get { return objectCollectionInitializers; }
+ set {
+ if (objectCollectionInitializers != value) {
+ objectCollectionInitializers = value;
+ OnPropertyChanged("ObjectCollectionInitializers");
+ }
+ }
+ }
+
+ bool showXmlDocumentation = true;
+
+ /// <summary>
+ /// Gets/Sets whether to include XML documentation comments in the decompiled code
+ /// </summary>
+ public bool ShowXmlDocumentation {
+ get { return showXmlDocumentation; }
+ set {
+ if (showXmlDocumentation != value) {
+ showXmlDocumentation = value;
+ OnPropertyChanged("ShowXmlDocumentation");
+ }
+ }
+ }
+
+ bool foldBraces = false;
+
+ public bool FoldBraces {
+ get { return foldBraces; }
+ set {
+ if (foldBraces != value) {
+ foldBraces = value;
+ OnPropertyChanged("FoldBraces");
+ }
+ }
+ }
+
+ #region Options to aid VB decompilation
+ bool introduceIncrementAndDecrement = true;
+
+ /// <summary>
+ /// Gets/Sets whether to use increment and decrement operators
+ /// </summary>
+ public bool IntroduceIncrementAndDecrement {
+ get { return introduceIncrementAndDecrement; }
+ set {
+ if (introduceIncrementAndDecrement != value) {
+ introduceIncrementAndDecrement = value;
+ OnPropertyChanged("IntroduceIncrementAndDecrement");
+ }
+ }
+ }
+
+ bool makeAssignmentExpressions = true;
+
+ /// <summary>
+ /// Gets/Sets whether to use assignment expressions such as in while ((count = Do()) != 0) ;
+ /// </summary>
+ public bool MakeAssignmentExpressions {
+ get { return makeAssignmentExpressions; }
+ set {
+ if (makeAssignmentExpressions != value) {
+ makeAssignmentExpressions = value;
+ OnPropertyChanged("MakeAssignmentExpressions");
+ }
+ }
+ }
+
+ bool alwaysGenerateExceptionVariableForCatchBlocks = false;
+
+ /// <summary>
+ /// Gets/Sets whether to always generate exception variables in catch blocks
+ /// </summary>
+ public bool AlwaysGenerateExceptionVariableForCatchBlocks {
+ get { return alwaysGenerateExceptionVariableForCatchBlocks; }
+ set {
+ if (alwaysGenerateExceptionVariableForCatchBlocks != value) {
+ alwaysGenerateExceptionVariableForCatchBlocks = value;
+ OnPropertyChanged("AlwaysGenerateExceptionVariableForCatchBlocks");
+ }
+ }
+ }
+ #endregion
+
+ CSharpFormattingOptions csharpFormattingOptions;
+
+ public CSharpFormattingOptions CSharpFormattingOptions {
+ get {
+ if (csharpFormattingOptions == null) {
+ csharpFormattingOptions = FormattingOptionsFactory.CreateAllman();
+ csharpFormattingOptions.IndentSwitchBody = false;
+ csharpFormattingOptions.ArrayInitializerWrapping = Wrapping.WrapAlways;
+ }
+ return csharpFormattingOptions;
+ }
+ set {
+ if (value == null)
+ throw new ArgumentNullException();
+ if (csharpFormattingOptions != value) {
+ csharpFormattingOptions = value;
+ OnPropertyChanged("CSharpFormattingOptions");
+ }
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected virtual void OnPropertyChanged(string propertyName)
+ {
+ if (PropertyChanged != null) {
+ PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+
+ public DecompilerSettings Clone()
+ {
+ DecompilerSettings settings = (DecompilerSettings)MemberwiseClone();
+ if (csharpFormattingOptions != null)
+ settings.csharpFormattingOptions = csharpFormattingOptions.Clone();
+ settings.PropertyChanged = null;
+ return settings;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
new file mode 100644
index 00000000..ea6e54db
--- /dev/null
+++ b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
@@ -0,0 +1,447 @@
+// 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 Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.Disassembler
+{
+ public enum ILNameSyntax
+ {
+ /// <summary>
+ /// class/valuetype + TypeName (built-in types use keyword syntax)
+ /// </summary>
+ Signature,
+ /// <summary>
+ /// Like signature, but always refers to type parameters using their position
+ /// </summary>
+ SignatureNoNamedTypeParameters,
+ /// <summary>
+ /// [assembly]Full.Type.Name (even for built-in types)
+ /// </summary>
+ TypeName,
+ /// <summary>
+ /// Name (but built-in types use keyword syntax)
+ /// </summary>
+ ShortTypeName
+ }
+
+ public static class DisassemblerHelpers
+ {
+ public static void WriteOffsetReference(ITextOutput writer, Instruction instruction)
+ {
+ writer.WriteReference(CecilExtensions.OffsetToString(instruction.Offset), instruction);
+ }
+
+ public static void WriteTo(this ExceptionHandler exceptionHandler, ITextOutput writer)
+ {
+ writer.Write("Try ");
+ WriteOffsetReference(writer, exceptionHandler.TryStart);
+ writer.Write('-');
+ WriteOffsetReference(writer, exceptionHandler.TryEnd);
+ writer.Write(' ');
+ writer.Write(exceptionHandler.HandlerType.ToString());
+ if (exceptionHandler.FilterStart != null) {
+ writer.Write(' ');
+ WriteOffsetReference(writer, exceptionHandler.FilterStart);
+ writer.Write(" handler ");
+ }
+ if (exceptionHandler.CatchType != null) {
+ writer.Write(' ');
+ exceptionHandler.CatchType.WriteTo(writer);
+ }
+ writer.Write(' ');
+ WriteOffsetReference(writer, exceptionHandler.HandlerStart);
+ writer.Write('-');
+ WriteOffsetReference(writer, exceptionHandler.HandlerEnd);
+ }
+
+ public static void WriteTo(this Instruction instruction, ITextOutput writer)
+ {
+ writer.WriteDefinition(CecilExtensions.OffsetToString(instruction.Offset), instruction);
+ writer.Write(": ");
+ writer.WriteReference(instruction.OpCode.Name, instruction.OpCode);
+ if (instruction.Operand != null) {
+ writer.Write(' ');
+ if (instruction.OpCode == OpCodes.Ldtoken) {
+ if (instruction.Operand is MethodReference)
+ writer.Write("method ");
+ else if (instruction.Operand is FieldReference)
+ writer.Write("field ");
+ }
+ WriteOperand(writer, instruction.Operand);
+ }
+ }
+
+ static void WriteLabelList(ITextOutput writer, Instruction[] instructions)
+ {
+ writer.Write("(");
+ for(int i = 0; i < instructions.Length; i++) {
+ if(i != 0) writer.Write(", ");
+ WriteOffsetReference(writer, instructions[i]);
+ }
+ writer.Write(")");
+ }
+
+ static string ToInvariantCultureString(object value)
+ {
+ IConvertible convertible = value as IConvertible;
+ return(null != convertible)
+ ? convertible.ToString(System.Globalization.CultureInfo.InvariantCulture)
+ : value.ToString();
+ }
+
+ public static void WriteTo(this MethodReference method, ITextOutput writer)
+ {
+ if (method.ExplicitThis) {
+ writer.Write("instance explicit ");
+ }
+ else if (method.HasThis) {
+ writer.Write("instance ");
+ }
+ method.ReturnType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
+ writer.Write(' ');
+ if (method.DeclaringType != null) {
+ method.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
+ writer.Write("::");
+ }
+ MethodDefinition md = method as MethodDefinition;
+ if (md != null && md.IsCompilerControlled) {
+ writer.WriteReference(Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8")), method);
+ } else {
+ writer.WriteReference(Escape(method.Name), method);
+ }
+ GenericInstanceMethod gim = method as GenericInstanceMethod;
+ if (gim != null) {
+ writer.Write('<');
+ for (int i = 0; i < gim.GenericArguments.Count; i++) {
+ if (i > 0)
+ writer.Write(", ");
+ gim.GenericArguments[i].WriteTo(writer);
+ }
+ writer.Write('>');
+ }
+ writer.Write("(");
+ var parameters = method.Parameters;
+ for(int i = 0; i < parameters.Count; ++i) {
+ if (i > 0) writer.Write(", ");
+ parameters[i].ParameterType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
+ }
+ writer.Write(")");
+ }
+
+ static void WriteTo(this FieldReference field, ITextOutput writer)
+ {
+ field.FieldType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
+ writer.Write(' ');
+ field.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
+ writer.Write("::");
+ writer.WriteReference(Escape(field.Name), field);
+ }
+
+ static bool IsValidIdentifierCharacter(char c)
+ {
+ return c == '_' || c == '$' || c == '@' || c == '?' || c == '`';
+ }
+
+ static bool IsValidIdentifier(string identifier)
+ {
+ if (string.IsNullOrEmpty(identifier))
+ return false;
+ if (!(char.IsLetter(identifier[0]) || IsValidIdentifierCharacter(identifier[0]))) {
+ // As a special case, .ctor and .cctor are valid despite starting with a dot
+ return identifier == ".ctor" || identifier == ".cctor";
+ }
+ for (int i = 1; i < identifier.Length; i++) {
+ if (!(char.IsLetterOrDigit(identifier[i]) || IsValidIdentifierCharacter(identifier[i]) || identifier[i] == '.'))
+ return false;
+ }
+ return true;
+ }
+
+ static readonly HashSet<string> ilKeywords = BuildKeywordList(
+ "abstract", "algorithm", "alignment", "ansi", "any", "arglist",
+ "array", "as", "assembly", "assert", "at", "auto", "autochar", "beforefieldinit",
+ "blob", "blob_object", "bool", "brnull", "brnull.s", "brzero", "brzero.s", "bstr",
+ "bytearray", "byvalstr", "callmostderived", "carray", "catch", "cdecl", "cf",
+ "char", "cil", "class", "clsid", "const", "currency", "custom", "date", "decimal",
+ "default", "demand", "deny", "endmac", "enum", "error", "explicit", "extends", "extern",
+ "false", "famandassem", "family", "famorassem", "fastcall", "fault", "field", "filetime",
+ "filter", "final", "finally", "fixed", "float", "float32", "float64", "forwardref",
+ "fromunmanaged", "handler", "hidebysig", "hresult", "idispatch", "il", "illegal",
+ "implements", "implicitcom", "implicitres", "import", "in", "inheritcheck", "init",
+ "initonly", "instance", "int", "int16", "int32", "int64", "int8", "interface", "internalcall",
+ "iunknown", "lasterr", "lcid", "linkcheck", "literal", "localloc", "lpstr", "lpstruct", "lptstr",
+ "lpvoid", "lpwstr", "managed", "marshal", "method", "modopt", "modreq", "native", "nested",
+ "newslot", "noappdomain", "noinlining", "nomachine", "nomangle", "nometadata", "noncasdemand",
+ "noncasinheritance", "noncaslinkdemand", "noprocess", "not", "not_in_gc_heap", "notremotable",
+ "notserialized", "null", "nullref", "object", "objectref", "opt", "optil", "out",
+ "permitonly", "pinned", "pinvokeimpl", "prefix1", "prefix2", "prefix3", "prefix4", "prefix5", "prefix6",
+ "prefix7", "prefixref", "prejitdeny", "prejitgrant", "preservesig", "private", "privatescope", "protected",
+ "public", "record", "refany", "reqmin", "reqopt", "reqrefuse", "reqsecobj", "request", "retval",
+ "rtspecialname", "runtime", "safearray", "sealed", "sequential", "serializable", "special", "specialname",
+ "static", "stdcall", "storage", "stored_object", "stream", "streamed_object", "string", "struct",
+ "synchronized", "syschar", "sysstring", "tbstr", "thiscall", "tls", "to", "true", "typedref",
+ "unicode", "unmanaged", "unmanagedexp", "unsigned", "unused", "userdefined", "value", "valuetype",
+ "vararg", "variant", "vector", "virtual", "void", "wchar", "winapi", "with", "wrapper",
+
+ // These are not listed as keywords in spec, but ILAsm treats them as such
+ "property", "type", "flags", "callconv", "strict"
+ );
+
+ static HashSet<string> BuildKeywordList(params string[] keywords)
+ {
+ HashSet<string> s = new HashSet<string>(keywords);
+ foreach (var field in typeof(OpCodes).GetFields()) {
+ s.Add(((OpCode)field.GetValue(null)).Name);
+ }
+ return s;
+ }
+
+ public static string Escape(string identifier)
+ {
+ if (IsValidIdentifier(identifier) && !ilKeywords.Contains(identifier)) {
+ return identifier;
+ } else {
+ // The ECMA specification says that ' inside SQString should be ecaped using an octal escape sequence,
+ // but we follow Microsoft's ILDasm and use \'.
+ return "'" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(identifier).Replace("'", "\\'") + "'";
+ }
+ }
+
+ public static void WriteTo(this TypeReference type, ITextOutput writer, ILNameSyntax syntax = ILNameSyntax.Signature)
+ {
+ ILNameSyntax syntaxForElementTypes = syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature;
+ if (type is PinnedType) {
+ ((PinnedType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
+ writer.Write(" pinned");
+ } else if (type is ArrayType) {
+ ArrayType at = (ArrayType)type;
+ at.ElementType.WriteTo(writer, syntaxForElementTypes);
+ writer.Write('[');
+ writer.Write(string.Join(", ", at.Dimensions));
+ writer.Write(']');
+ } else if (type is GenericParameter) {
+ writer.Write('!');
+ if (((GenericParameter)type).Owner.GenericParameterType == GenericParameterType.Method)
+ writer.Write('!');
+ if (string.IsNullOrEmpty(type.Name) || type.Name[0] == '!' || syntax == ILNameSyntax.SignatureNoNamedTypeParameters)
+ writer.Write(((GenericParameter)type).Position.ToString());
+ else
+ writer.Write(Escape(type.Name));
+ } else if (type is ByReferenceType) {
+ ((ByReferenceType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
+ writer.Write('&');
+ } else if (type is PointerType) {
+ ((PointerType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
+ writer.Write('*');
+ } else if (type is GenericInstanceType) {
+ type.GetElementType().WriteTo(writer, syntaxForElementTypes);
+ writer.Write('<');
+ var arguments = ((GenericInstanceType)type).GenericArguments;
+ for (int i = 0; i < arguments.Count; i++) {
+ if (i > 0)
+ writer.Write(", ");
+ arguments[i].WriteTo(writer, syntaxForElementTypes);
+ }
+ writer.Write('>');
+ } else if (type is OptionalModifierType) {
+ ((OptionalModifierType)type).ElementType.WriteTo(writer, syntax);
+ writer.Write(" modopt(");
+ ((OptionalModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName);
+ writer.Write(") ");
+ } else if (type is RequiredModifierType) {
+ ((RequiredModifierType)type).ElementType.WriteTo(writer, syntax);
+ writer.Write(" modreq(");
+ ((RequiredModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName);
+ writer.Write(") ");
+ } else {
+ string name = PrimitiveTypeName(type.FullName);
+ if (syntax == ILNameSyntax.ShortTypeName) {
+ if (name != null)
+ writer.Write(name);
+ else
+ writer.WriteReference(Escape(type.Name), type);
+ } else if ((syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) && name != null) {
+ writer.Write(name);
+ } else {
+ if (syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters)
+ writer.Write(type.IsValueType ? "valuetype " : "class ");
+
+ if (type.DeclaringType != null) {
+ type.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
+ writer.Write('/');
+ writer.WriteReference(Escape(type.Name), type);
+ } else {
+ if (!type.IsDefinition && type.Scope != null && !(type is TypeSpecification))
+ writer.Write("[{0}]", Escape(type.Scope.Name));
+ writer.WriteReference(Escape(type.FullName), type);
+ }
+ }
+ }
+ }
+
+ public static void WriteOperand(ITextOutput writer, object operand)
+ {
+ if (operand == null)
+ throw new ArgumentNullException("operand");
+
+ Instruction targetInstruction = operand as Instruction;
+ if (targetInstruction != null) {
+ WriteOffsetReference(writer, targetInstruction);
+ return;
+ }
+
+ Instruction[] targetInstructions = operand as Instruction[];
+ if (targetInstructions != null) {
+ WriteLabelList(writer, targetInstructions);
+ return;
+ }
+
+ VariableReference variableRef = operand as VariableReference;
+ if (variableRef != null) {
+ if (string.IsNullOrEmpty(variableRef.Name))
+ writer.WriteReference(variableRef.Index.ToString(), variableRef);
+ else
+ writer.WriteReference(Escape(variableRef.Name), variableRef);
+ return;
+ }
+
+ ParameterReference paramRef = operand as ParameterReference;
+ if (paramRef != null) {
+ if (string.IsNullOrEmpty(paramRef.Name))
+ writer.WriteReference(paramRef.Index.ToString(), paramRef);
+ else
+ writer.WriteReference(Escape(paramRef.Name), paramRef);
+ return;
+ }
+
+ MethodReference methodRef = operand as MethodReference;
+ if (methodRef != null) {
+ methodRef.WriteTo(writer);
+ return;
+ }
+
+ TypeReference typeRef = operand as TypeReference;
+ if (typeRef != null) {
+ typeRef.WriteTo(writer, ILNameSyntax.TypeName);
+ return;
+ }
+
+ FieldReference fieldRef = operand as FieldReference;
+ if (fieldRef != null) {
+ fieldRef.WriteTo(writer);
+ return;
+ }
+
+ string s = operand as string;
+ if (s != null) {
+ writer.Write("\"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(s) + "\"");
+ } else if (operand is char) {
+ writer.Write(((int)(char)operand).ToString());
+ } else if (operand is float) {
+ float val = (float)operand;
+ if (val == 0) {
+ if (1 / val == float.NegativeInfinity) {
+ // negative zero is a special case
+ writer.Write('-');
+ }
+ writer.Write("0.0");
+ } else if (float.IsInfinity(val) || float.IsNaN(val)) {
+ byte[] data = BitConverter.GetBytes(val);
+ writer.Write('(');
+ for (int i = 0; i < data.Length; i++) {
+ if (i > 0)
+ writer.Write(' ');
+ writer.Write(data[i].ToString("X2"));
+ }
+ writer.Write(')');
+ } else {
+ writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture));
+ }
+ } else if (operand is double) {
+ double val = (double)operand;
+ if (val == 0) {
+ if (1 / val == double.NegativeInfinity) {
+ // negative zero is a special case
+ writer.Write('-');
+ }
+ writer.Write("0.0");
+ } else if (double.IsInfinity(val) || double.IsNaN(val)) {
+ byte[] data = BitConverter.GetBytes(val);
+ writer.Write('(');
+ for (int i = 0; i < data.Length; i++) {
+ if (i > 0)
+ writer.Write(' ');
+ writer.Write(data[i].ToString("X2"));
+ }
+ writer.Write(')');
+ } else {
+ writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture));
+ }
+ } else if (operand is bool) {
+ writer.Write((bool)operand ? "true" : "false");
+ } else {
+ s = ToInvariantCultureString(operand);
+ writer.Write(s);
+ }
+ }
+
+ public static string PrimitiveTypeName(string fullName)
+ {
+ switch (fullName) {
+ case "System.SByte":
+ return "int8";
+ case "System.Int16":
+ return "int16";
+ case "System.Int32":
+ return "int32";
+ case "System.Int64":
+ return "int64";
+ case "System.Byte":
+ return "uint8";
+ case "System.UInt16":
+ return "uint16";
+ case "System.UInt32":
+ return "uint32";
+ case "System.UInt64":
+ return "uint64";
+ case "System.Single":
+ return "float32";
+ case "System.Double":
+ return "float64";
+ case "System.Void":
+ return "void";
+ case "System.Boolean":
+ return "bool";
+ case "System.String":
+ return "string";
+ case "System.Char":
+ return "char";
+ case "System.Object":
+ return "object";
+ case "System.IntPtr":
+ return "native int";
+ default:
+ return null;
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Disassembler/ILStructure.cs b/ICSharpCode.Decompiler/Disassembler/ILStructure.cs
new file mode 100644
index 00000000..ff9285b2
--- /dev/null
+++ b/ICSharpCode.Decompiler/Disassembler/ILStructure.cs
@@ -0,0 +1,228 @@
+// 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.FlowAnalysis;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.Disassembler
+{
+ /// <summary>
+ /// Specifies the type of an IL structure.
+ /// </summary>
+ public enum ILStructureType
+ {
+ /// <summary>
+ /// The root block of the method
+ /// </summary>
+ Root,
+ /// <summary>
+ /// A nested control structure representing a loop.
+ /// </summary>
+ Loop,
+ /// <summary>
+ /// A nested control structure representing a try block.
+ /// </summary>
+ Try,
+ /// <summary>
+ /// A nested control structure representing a catch, finally, or fault block.
+ /// </summary>
+ Handler,
+ /// <summary>
+ /// A nested control structure representing an exception filter block.
+ /// </summary>
+ Filter
+ }
+
+ /// <summary>
+ /// An IL structure.
+ /// </summary>
+ public class ILStructure
+ {
+ public readonly ILStructureType Type;
+
+ /// <summary>
+ /// Start position of the structure.
+ /// </summary>
+ public readonly int StartOffset;
+
+ /// <summary>
+ /// End position of the structure. (exclusive)
+ /// </summary>
+ public readonly int EndOffset;
+
+ /// <summary>
+ /// The exception handler associated with the Try, Filter or Handler block.
+ /// </summary>
+ public readonly ExceptionHandler ExceptionHandler;
+
+ /// <summary>
+ /// The loop's entry point.
+ /// </summary>
+ public readonly Instruction LoopEntryPoint;
+
+ /// <summary>
+ /// The list of child structures.
+ /// </summary>
+ public readonly List<ILStructure> Children = new List<ILStructure>();
+
+ public ILStructure(MethodBody body)
+ : this(ILStructureType.Root, 0, body.CodeSize)
+ {
+ // Build the tree of exception structures:
+ for (int i = 0; i < body.ExceptionHandlers.Count; i++) {
+ ExceptionHandler eh = body.ExceptionHandlers[i];
+ if (!body.ExceptionHandlers.Take(i).Any(oldEh => oldEh.TryStart == eh.TryStart && oldEh.TryEnd == eh.TryEnd))
+ AddNestedStructure(new ILStructure(ILStructureType.Try, eh.TryStart.Offset, eh.TryEnd.Offset, eh));
+ if (eh.HandlerType == ExceptionHandlerType.Filter)
+ AddNestedStructure(new ILStructure(ILStructureType.Filter, eh.FilterStart.Offset, eh.HandlerStart.Offset, eh));
+ AddNestedStructure(new ILStructure(ILStructureType.Handler, eh.HandlerStart.Offset, eh.HandlerEnd == null ? body.CodeSize : eh.HandlerEnd.Offset, eh));
+ }
+ // Very simple loop detection: look for backward branches
+ List<KeyValuePair<Instruction, Instruction>> allBranches = FindAllBranches(body);
+ // We go through the branches in reverse so that we find the biggest possible loop boundary first (think loops with "continue;")
+ for (int i = allBranches.Count - 1; i >= 0; i--) {
+ int loopEnd = allBranches[i].Key.GetEndOffset();
+ int loopStart = allBranches[i].Value.Offset;
+ if (loopStart < loopEnd) {
+ // We found a backward branch. This is a potential loop.
+ // Check that is has only one entry point:
+ Instruction entryPoint = null;
+
+ // entry point is first instruction in loop if prev inst isn't an unconditional branch
+ Instruction prev = allBranches[i].Value.Previous;
+ if (prev != null && !OpCodeInfo.IsUnconditionalBranch(prev.OpCode))
+ entryPoint = allBranches[i].Value;
+
+ bool multipleEntryPoints = false;
+ foreach (var pair in allBranches) {
+ if (pair.Key.Offset < loopStart || pair.Key.Offset >= loopEnd) {
+ if (loopStart <= pair.Value.Offset && pair.Value.Offset < loopEnd) {
+ // jump from outside the loop into the loop
+ if (entryPoint == null)
+ entryPoint = pair.Value;
+ else if (pair.Value != entryPoint)
+ multipleEntryPoints = true;
+ }
+ }
+ }
+ if (!multipleEntryPoints) {
+ AddNestedStructure(new ILStructure(ILStructureType.Loop, loopStart, loopEnd, entryPoint));
+ }
+ }
+ }
+ SortChildren();
+ }
+
+ public ILStructure(ILStructureType type, int startOffset, int endOffset, ExceptionHandler handler = null)
+ {
+ Debug.Assert(startOffset < endOffset);
+ Type = type;
+ StartOffset = startOffset;
+ EndOffset = endOffset;
+ ExceptionHandler = handler;
+ }
+
+ public ILStructure(ILStructureType type, int startOffset, int endOffset, Instruction loopEntryPoint)
+ {
+ Debug.Assert(startOffset < endOffset);
+ Type = type;
+ StartOffset = startOffset;
+ EndOffset = endOffset;
+ LoopEntryPoint = loopEntryPoint;
+ }
+
+ bool AddNestedStructure(ILStructure newStructure)
+ {
+ // special case: don't consider the loop-like structure of "continue;" statements to be nested loops
+ if (Type == ILStructureType.Loop && newStructure.Type == ILStructureType.Loop && newStructure.StartOffset == StartOffset)
+ return false;
+
+ // use <= for end-offset comparisons because both end and EndOffset are exclusive
+ Debug.Assert(StartOffset <= newStructure.StartOffset && newStructure.EndOffset <= EndOffset);
+ foreach (ILStructure child in Children) {
+ if (child.StartOffset <= newStructure.StartOffset && newStructure.EndOffset <= child.EndOffset) {
+ return child.AddNestedStructure(newStructure);
+ } else if (!(child.EndOffset <= newStructure.StartOffset || newStructure.EndOffset <= child.StartOffset)) {
+ // child and newStructure overlap
+ if (!(newStructure.StartOffset <= child.StartOffset && child.EndOffset <= newStructure.EndOffset)) {
+ // Invalid nesting, can't build a tree. -> Don't add the new structure.
+ return false;
+ }
+ }
+ }
+ // Move existing structures into the new structure:
+ for (int i = 0; i < Children.Count; i++) {
+ ILStructure child = Children[i];
+ if (newStructure.StartOffset <= child.StartOffset && child.EndOffset <= newStructure.EndOffset) {
+ Children.RemoveAt(i--);
+ newStructure.Children.Add(child);
+ }
+ }
+ // Add the structure here:
+ Children.Add(newStructure);
+ return true;
+ }
+
+ /// <summary>
+ /// Finds all branches. Returns list of source offset->target offset mapping.
+ /// Multiple entries for the same source offset are possible (switch statements).
+ /// The result is sorted by source offset.
+ /// </summary>
+ List<KeyValuePair<Instruction, Instruction>> FindAllBranches(MethodBody body)
+ {
+ var result = new List<KeyValuePair<Instruction, Instruction>>();
+ foreach (Instruction inst in body.Instructions) {
+ switch (inst.OpCode.OperandType) {
+ case OperandType.InlineBrTarget:
+ case OperandType.ShortInlineBrTarget:
+ result.Add(new KeyValuePair<Instruction, Instruction>(inst, (Instruction)inst.Operand));
+ break;
+ case OperandType.InlineSwitch:
+ foreach (Instruction target in (Instruction[])inst.Operand)
+ result.Add(new KeyValuePair<Instruction, Instruction>(inst, target));
+ break;
+ }
+ }
+ return result;
+ }
+
+ void SortChildren()
+ {
+ Children.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset));
+ foreach (ILStructure child in Children)
+ child.SortChildren();
+ }
+
+ /// <summary>
+ /// Gets the innermost structure containing the specified offset.
+ /// </summary>
+ public ILStructure GetInnermost(int offset)
+ {
+ Debug.Assert(StartOffset <= offset && offset < EndOffset);
+ foreach (ILStructure child in Children) {
+ if (child.StartOffset <= offset && offset < child.EndOffset)
+ return child.GetInnermost(offset);
+ }
+ return this;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
new file mode 100644
index 00000000..2b5db78b
--- /dev/null
+++ b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
@@ -0,0 +1,239 @@
+// 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.Threading;
+
+using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.FlowAnalysis;
+using ICSharpCode.Decompiler.ILAst;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.Disassembler
+{
+ /// <summary>
+ /// Disassembles a method body.
+ /// </summary>
+ public sealed class MethodBodyDisassembler
+ {
+ readonly ITextOutput output;
+ readonly bool detectControlStructure;
+ readonly CancellationToken cancellationToken;
+
+ public MethodBodyDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken)
+ {
+ if (output == null)
+ throw new ArgumentNullException("output");
+ this.output = output;
+ this.detectControlStructure = detectControlStructure;
+ this.cancellationToken = cancellationToken;
+ }
+
+ public void Disassemble(MethodBody body, MethodDebugSymbols debugSymbols)
+ {
+ // start writing IL code
+ MethodDefinition method = body.Method;
+ output.WriteLine("// Method begins at RVA 0x{0:x4}", method.RVA);
+ output.WriteLine("// Code size {0} (0x{0:x})", body.CodeSize);
+ output.WriteLine(".maxstack {0}", body.MaxStackSize);
+ if (method.DeclaringType.Module.Assembly != null && method.DeclaringType.Module.Assembly.EntryPoint == method)
+ output.WriteLine (".entrypoint");
+
+ if (method.Body.HasVariables) {
+ output.Write(".locals ");
+ if (method.Body.InitLocals)
+ output.Write("init ");
+ output.WriteLine("(");
+ output.Indent();
+ foreach (var v in method.Body.Variables) {
+ output.WriteDefinition("[" + v.Index + "] ", v);
+ v.VariableType.WriteTo(output);
+ if (!string.IsNullOrEmpty(v.Name)) {
+ output.Write(' ');
+ output.Write(DisassemblerHelpers.Escape(v.Name));
+ }
+ if (v.Index + 1 < method.Body.Variables.Count)
+ output.Write(',');
+ output.WriteLine();
+ }
+ output.Unindent();
+ output.WriteLine(")");
+ }
+ output.WriteLine();
+
+ if (detectControlStructure && body.Instructions.Count > 0) {
+ Instruction inst = body.Instructions[0];
+ HashSet<int> branchTargets = GetBranchTargets(body.Instructions);
+ WriteStructureBody(new ILStructure(body), branchTargets, ref inst, debugSymbols, method.Body.CodeSize);
+ } else {
+ foreach (var inst in method.Body.Instructions) {
+ var startLocation = output.Location;
+ inst.WriteTo(output);
+
+ if (debugSymbols != null) {
+ // add IL code mappings - used in debugger
+ debugSymbols.SequencePoints.Add(
+ new SequencePoint() {
+ StartLocation = output.Location,
+ EndLocation = output.Location,
+ ILRanges = new ILRange[] { new ILRange(inst.Offset, inst.Next == null ? method.Body.CodeSize : inst.Next.Offset) }
+ });
+ }
+
+ output.WriteLine();
+ }
+ if (method.Body.HasExceptionHandlers) {
+ output.WriteLine();
+ foreach (var eh in method.Body.ExceptionHandlers) {
+ eh.WriteTo(output);
+ output.WriteLine();
+ }
+ }
+ }
+ }
+
+ HashSet<int> GetBranchTargets(IEnumerable<Instruction> instructions)
+ {
+ HashSet<int> branchTargets = new HashSet<int>();
+ foreach (var inst in instructions) {
+ Instruction target = inst.Operand as Instruction;
+ if (target != null)
+ branchTargets.Add(target.Offset);
+ Instruction[] targets = inst.Operand as Instruction[];
+ if (targets != null)
+ foreach (Instruction t in targets)
+ branchTargets.Add(t.Offset);
+ }
+ return branchTargets;
+ }
+
+ void WriteStructureHeader(ILStructure s)
+ {
+ switch (s.Type) {
+ case ILStructureType.Loop:
+ output.Write("// loop start");
+ if (s.LoopEntryPoint != null) {
+ output.Write(" (head: ");
+ DisassemblerHelpers.WriteOffsetReference(output, s.LoopEntryPoint);
+ output.Write(')');
+ }
+ output.WriteLine();
+ break;
+ case ILStructureType.Try:
+ output.WriteLine(".try");
+ output.WriteLine("{");
+ break;
+ case ILStructureType.Handler:
+ switch (s.ExceptionHandler.HandlerType) {
+ case ExceptionHandlerType.Catch:
+ case ExceptionHandlerType.Filter:
+ output.Write("catch");
+ if (s.ExceptionHandler.CatchType != null) {
+ output.Write(' ');
+ s.ExceptionHandler.CatchType.WriteTo(output, ILNameSyntax.TypeName);
+ }
+ output.WriteLine();
+ break;
+ case ExceptionHandlerType.Finally:
+ output.WriteLine("finally");
+ break;
+ case ExceptionHandlerType.Fault:
+ output.WriteLine("fault");
+ break;
+ default:
+ throw new NotSupportedException();
+ }
+ output.WriteLine("{");
+ break;
+ case ILStructureType.Filter:
+ output.WriteLine("filter");
+ output.WriteLine("{");
+ break;
+ default:
+ throw new NotSupportedException();
+ }
+ output.Indent();
+ }
+
+ void WriteStructureBody(ILStructure s, HashSet<int> branchTargets, ref Instruction inst, MethodDebugSymbols debugSymbols, int codeSize)
+ {
+ bool isFirstInstructionInStructure = true;
+ bool prevInstructionWasBranch = false;
+ int childIndex = 0;
+ while (inst != null && inst.Offset < s.EndOffset) {
+ int offset = inst.Offset;
+ if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) {
+ ILStructure child = s.Children[childIndex++];
+ WriteStructureHeader(child);
+ WriteStructureBody(child, branchTargets, ref inst, debugSymbols, codeSize);
+ WriteStructureFooter(child);
+ } else {
+ if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) {
+ output.WriteLine(); // put an empty line after branches, and in front of branch targets
+ }
+ var startLocation = output.Location;
+ inst.WriteTo(output);
+
+ // add IL code mappings - used in debugger
+ if (debugSymbols != null) {
+ debugSymbols.SequencePoints.Add(
+ new SequencePoint() {
+ StartLocation = startLocation,
+ EndLocation = output.Location,
+ ILRanges = new ILRange[] { new ILRange(inst.Offset, inst.Next == null ? codeSize : inst.Next.Offset) }
+ });
+ }
+
+ output.WriteLine();
+
+ prevInstructionWasBranch = inst.OpCode.FlowControl == FlowControl.Branch
+ || inst.OpCode.FlowControl == FlowControl.Cond_Branch
+ || inst.OpCode.FlowControl == FlowControl.Return
+ || inst.OpCode.FlowControl == FlowControl.Throw;
+
+ inst = inst.Next;
+ }
+ isFirstInstructionInStructure = false;
+ }
+ }
+
+ void WriteStructureFooter(ILStructure s)
+ {
+ output.Unindent();
+ switch (s.Type) {
+ case ILStructureType.Loop:
+ output.WriteLine("// end loop");
+ break;
+ case ILStructureType.Try:
+ output.WriteLine("} // end .try");
+ break;
+ case ILStructureType.Handler:
+ output.WriteLine("} // end handler");
+ break;
+ case ILStructureType.Filter:
+ output.WriteLine("} // end filter");
+ break;
+ default:
+ throw new NotSupportedException();
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
new file mode 100644
index 00000000..2a374c3a
--- /dev/null
+++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
@@ -0,0 +1,1168 @@
+// 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.Text;
+using System.Threading;
+using ICSharpCode.NRefactory;
+using Mono.Cecil;
+using Mono.Collections.Generic;
+
+namespace ICSharpCode.Decompiler.Disassembler
+{
+ /// <summary>
+ /// Disassembles type and member definitions.
+ /// </summary>
+ public sealed class ReflectionDisassembler
+ {
+ ITextOutput output;
+ CancellationToken cancellationToken;
+ bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings)
+ MethodBodyDisassembler methodBodyDisassembler;
+ MemberReference currentMember;
+
+ public ReflectionDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken)
+ {
+ if (output == null)
+ throw new ArgumentNullException("output");
+ this.output = output;
+ this.cancellationToken = cancellationToken;
+ methodBodyDisassembler = new MethodBodyDisassembler(output, detectControlStructure, cancellationToken);
+ }
+
+ #region Disassemble Method
+ EnumNameCollection<MethodAttributes> methodAttributeFlags = new EnumNameCollection<MethodAttributes>() {
+ { MethodAttributes.Final, "final" },
+ { MethodAttributes.HideBySig, "hidebysig" },
+ { MethodAttributes.SpecialName, "specialname" },
+ { MethodAttributes.PInvokeImpl, null }, // handled separately
+ { MethodAttributes.UnmanagedExport, "export" },
+ { MethodAttributes.RTSpecialName, "rtspecialname" },
+ { MethodAttributes.RequireSecObject, "reqsecobj" },
+ { MethodAttributes.NewSlot, "newslot" },
+ { MethodAttributes.CheckAccessOnOverride, "strict" },
+ { MethodAttributes.Abstract, "abstract" },
+ { MethodAttributes.Virtual, "virtual" },
+ { MethodAttributes.Static, "static" },
+ { MethodAttributes.HasSecurity, null }, // ?? also invisible in ILDasm
+ };
+
+ EnumNameCollection<MethodAttributes> methodVisibility = new EnumNameCollection<MethodAttributes>() {
+ { MethodAttributes.Private, "private" },
+ { MethodAttributes.FamANDAssem, "famandassem" },
+ { MethodAttributes.Assembly, "assembly" },
+ { MethodAttributes.Family, "family" },
+ { MethodAttributes.FamORAssem, "famorassem" },
+ { MethodAttributes.Public, "public" },
+ };
+
+ EnumNameCollection<MethodCallingConvention> callingConvention = new EnumNameCollection<MethodCallingConvention>() {
+ { MethodCallingConvention.C, "unmanaged cdecl" },
+ { MethodCallingConvention.StdCall, "unmanaged stdcall" },
+ { MethodCallingConvention.ThisCall, "unmanaged thiscall" },
+ { MethodCallingConvention.FastCall, "unmanaged fastcall" },
+ { MethodCallingConvention.VarArg, "vararg" },
+ { MethodCallingConvention.Generic, null },
+ };
+
+ EnumNameCollection<MethodImplAttributes> methodCodeType = new EnumNameCollection<MethodImplAttributes>() {
+ { MethodImplAttributes.IL, "cil" },
+ { MethodImplAttributes.Native, "native" },
+ { MethodImplAttributes.OPTIL, "optil" },
+ { MethodImplAttributes.Runtime, "runtime" },
+ };
+
+ EnumNameCollection<MethodImplAttributes> methodImpl = new EnumNameCollection<MethodImplAttributes>() {
+ { MethodImplAttributes.Synchronized, "synchronized" },
+ { MethodImplAttributes.NoInlining, "noinlining" },
+ { MethodImplAttributes.NoOptimization, "nooptimization" },
+ { MethodImplAttributes.PreserveSig, "preservesig" },
+ { MethodImplAttributes.InternalCall, "internalcall" },
+ { MethodImplAttributes.ForwardRef, "forwardref" },
+ };
+
+ public void DisassembleMethod(MethodDefinition method)
+ {
+ // set current member
+ currentMember = method;
+
+ // write method header
+ output.WriteDefinition(".method ", method);
+ DisassembleMethodInternal(method);
+ }
+
+ void DisassembleMethodInternal(MethodDefinition method)
+ {
+ // .method public hidebysig specialname
+ // instance default class [mscorlib]System.IO.TextWriter get_BaseWriter () cil managed
+ //
+
+ TextLocation startLocation = output.Location;
+
+ //emit flags
+ WriteEnum(method.Attributes & MethodAttributes.MemberAccessMask, methodVisibility);
+ WriteFlags(method.Attributes & ~MethodAttributes.MemberAccessMask, methodAttributeFlags);
+ if(method.IsCompilerControlled) output.Write("privatescope ");
+
+ if ((method.Attributes & MethodAttributes.PInvokeImpl) == MethodAttributes.PInvokeImpl) {
+ output.Write("pinvokeimpl");
+ if (method.HasPInvokeInfo && method.PInvokeInfo != null) {
+ PInvokeInfo info = method.PInvokeInfo;
+ output.Write("(\"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(info.Module.Name) + "\"");
+
+ if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != method.Name)
+ output.Write(" as \"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(info.EntryPoint) + "\"");
+
+ if (info.IsNoMangle)
+ output.Write(" nomangle");
+
+ if (info.IsCharSetAnsi)
+ output.Write(" ansi");
+ else if (info.IsCharSetAuto)
+ output.Write(" autochar");
+ else if (info.IsCharSetUnicode)
+ output.Write(" unicode");
+
+ if (info.SupportsLastError)
+ output.Write(" lasterr");
+
+ if (info.IsCallConvCdecl)
+ output.Write(" cdecl");
+ else if (info.IsCallConvFastcall)
+ output.Write(" fastcall");
+ else if (info.IsCallConvStdCall)
+ output.Write(" stdcall");
+ else if (info.IsCallConvThiscall)
+ output.Write(" thiscall");
+ else if (info.IsCallConvWinapi)
+ output.Write(" winapi");
+
+ output.Write(')');
+ }
+ output.Write(' ');
+ }
+
+ output.WriteLine();
+ output.Indent();
+ if (method.ExplicitThis) {
+ output.Write("instance explicit ");
+ } else if (method.HasThis) {
+ output.Write("instance ");
+ }
+
+ //call convention
+ WriteEnum(method.CallingConvention & (MethodCallingConvention)0x1f, callingConvention);
+
+ //return type
+ method.ReturnType.WriteTo(output);
+ output.Write(' ');
+ if (method.MethodReturnType.HasMarshalInfo) {
+ WriteMarshalInfo(method.MethodReturnType.MarshalInfo);
+ }
+
+ if (method.IsCompilerControlled) {
+ output.Write(DisassemblerHelpers.Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8")));
+ } else {
+ output.Write(DisassemblerHelpers.Escape(method.Name));
+ }
+
+ WriteTypeParameters(output, method);
+
+ //( params )
+ output.Write(" (");
+ if (method.HasParameters) {
+ output.WriteLine();
+ output.Indent();
+ WriteParameters(method.Parameters);
+ output.Unindent();
+ }
+ output.Write(") ");
+ //cil managed
+ WriteEnum(method.ImplAttributes & MethodImplAttributes.CodeTypeMask, methodCodeType);
+ if ((method.ImplAttributes & MethodImplAttributes.ManagedMask) == MethodImplAttributes.Managed)
+ output.Write("managed ");
+ else
+ output.Write("unmanaged ");
+ WriteFlags(method.ImplAttributes & ~(MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask), methodImpl);
+
+ output.Unindent();
+ OpenBlock(defaultCollapsed: isInType);
+ WriteAttributes(method.CustomAttributes);
+ if (method.HasOverrides) {
+ foreach (var methodOverride in method.Overrides) {
+ output.Write(".override method ");
+ methodOverride.WriteTo(output);
+ output.WriteLine();
+ }
+ }
+ foreach (var p in method.Parameters) {
+ WriteParameterAttributes(p);
+ }
+ WriteSecurityDeclarations(method);
+
+ if (method.HasBody) {
+ // create IL code mappings - used in debugger
+ MethodDebugSymbols debugSymbols = new MethodDebugSymbols(method);
+ debugSymbols.StartLocation = startLocation;
+ methodBodyDisassembler.Disassemble(method.Body, debugSymbols);
+ debugSymbols.EndLocation = output.Location;
+ output.AddDebugSymbols(debugSymbols);
+ }
+
+ CloseBlock("end of method " + DisassemblerHelpers.Escape(method.DeclaringType.Name) + "::" + DisassemblerHelpers.Escape(method.Name));
+ }
+
+ #region Write Security Declarations
+ void WriteSecurityDeclarations(ISecurityDeclarationProvider secDeclProvider)
+ {
+ if (!secDeclProvider.HasSecurityDeclarations)
+ return;
+ foreach (var secdecl in secDeclProvider.SecurityDeclarations) {
+ output.Write(".permissionset ");
+ switch (secdecl.Action) {
+ case SecurityAction.Request:
+ output.Write("request");
+ break;
+ case SecurityAction.Demand:
+ output.Write("demand");
+ break;
+ case SecurityAction.Assert:
+ output.Write("assert");
+ break;
+ case SecurityAction.Deny:
+ output.Write("deny");
+ break;
+ case SecurityAction.PermitOnly:
+ output.Write("permitonly");
+ break;
+ case SecurityAction.LinkDemand:
+ output.Write("linkcheck");
+ break;
+ case SecurityAction.InheritDemand:
+ output.Write("inheritcheck");
+ break;
+ case SecurityAction.RequestMinimum:
+ output.Write("reqmin");
+ break;
+ case SecurityAction.RequestOptional:
+ output.Write("reqopt");
+ break;
+ case SecurityAction.RequestRefuse:
+ output.Write("reqrefuse");
+ break;
+ case SecurityAction.PreJitGrant:
+ output.Write("prejitgrant");
+ break;
+ case SecurityAction.PreJitDeny:
+ output.Write("prejitdeny");
+ break;
+ case SecurityAction.NonCasDemand:
+ output.Write("noncasdemand");
+ break;
+ case SecurityAction.NonCasLinkDemand:
+ output.Write("noncaslinkdemand");
+ break;
+ case SecurityAction.NonCasInheritance:
+ output.Write("noncasinheritance");
+ break;
+ default:
+ output.Write(secdecl.Action.ToString());
+ break;
+ }
+ output.WriteLine(" = {");
+ output.Indent();
+ for (int i = 0; i < secdecl.SecurityAttributes.Count; i++) {
+ SecurityAttribute sa = secdecl.SecurityAttributes[i];
+ if (sa.AttributeType.Scope == sa.AttributeType.Module) {
+ output.Write("class ");
+ output.Write(DisassemblerHelpers.Escape(GetAssemblyQualifiedName(sa.AttributeType)));
+ } else {
+ sa.AttributeType.WriteTo(output, ILNameSyntax.TypeName);
+ }
+ output.Write(" = {");
+ if (sa.HasFields || sa.HasProperties) {
+ output.WriteLine();
+ output.Indent();
+
+ foreach (CustomAttributeNamedArgument na in sa.Fields) {
+ output.Write("field ");
+ WriteSecurityDeclarationArgument(na);
+ output.WriteLine();
+ }
+
+ foreach (CustomAttributeNamedArgument na in sa.Properties) {
+ output.Write("property ");
+ WriteSecurityDeclarationArgument(na);
+ output.WriteLine();
+ }
+
+ output.Unindent();
+ }
+ output.Write('}');
+
+ if (i + 1< secdecl.SecurityAttributes.Count)
+ output.Write(',');
+ output.WriteLine();
+ }
+ output.Unindent();
+ output.WriteLine("}");
+ }
+ }
+
+ void WriteSecurityDeclarationArgument(CustomAttributeNamedArgument na)
+ {
+ TypeReference type = na.Argument.Type;
+ if (type.MetadataType == MetadataType.Class || type.MetadataType == MetadataType.ValueType) {
+ output.Write("enum ");
+ if (type.Scope != type.Module) {
+ output.Write("class ");
+ output.Write(DisassemblerHelpers.Escape(GetAssemblyQualifiedName(type)));
+ } else {
+ type.WriteTo(output, ILNameSyntax.TypeName);
+ }
+ } else {
+ type.WriteTo(output);
+ }
+ output.Write(' ');
+ output.Write(DisassemblerHelpers.Escape(na.Name));
+ output.Write(" = ");
+ if (na.Argument.Value is string) {
+ // secdecls use special syntax for strings
+ output.Write("string('{0}')", NRefactory.CSharp.TextWriterTokenWriter.ConvertString((string)na.Argument.Value).Replace("'", "\'"));
+ } else {
+ WriteConstant(na.Argument.Value);
+ }
+ }
+
+ string GetAssemblyQualifiedName(TypeReference type)
+ {
+ AssemblyNameReference anr = type.Scope as AssemblyNameReference;
+ if (anr == null) {
+ ModuleDefinition md = type.Scope as ModuleDefinition;
+ if (md != null) {
+ anr = md.Assembly.Name;
+ }
+ }
+ if (anr != null) {
+ return type.FullName + ", " + anr.FullName;
+ } else {
+ return type.FullName;
+ }
+ }
+ #endregion
+
+ #region WriteMarshalInfo
+ void WriteMarshalInfo(MarshalInfo marshalInfo)
+ {
+ output.Write("marshal(");
+ WriteNativeType(marshalInfo.NativeType, marshalInfo);
+ output.Write(") ");
+ }
+
+ void WriteNativeType(NativeType nativeType, MarshalInfo marshalInfo = null)
+ {
+ switch (nativeType) {
+ case NativeType.None:
+ break;
+ case NativeType.Boolean:
+ output.Write("bool");
+ break;
+ case NativeType.I1:
+ output.Write("int8");
+ break;
+ case NativeType.U1:
+ output.Write("unsigned int8");
+ break;
+ case NativeType.I2:
+ output.Write("int16");
+ break;
+ case NativeType.U2:
+ output.Write("unsigned int16");
+ break;
+ case NativeType.I4:
+ output.Write("int32");
+ break;
+ case NativeType.U4:
+ output.Write("unsigned int32");
+ break;
+ case NativeType.I8:
+ output.Write("int64");
+ break;
+ case NativeType.U8:
+ output.Write("unsigned int64");
+ break;
+ case NativeType.R4:
+ output.Write("float32");
+ break;
+ case NativeType.R8:
+ output.Write("float64");
+ break;
+ case NativeType.LPStr:
+ output.Write("lpstr");
+ break;
+ case NativeType.Int:
+ output.Write("int");
+ break;
+ case NativeType.UInt:
+ output.Write("unsigned int");
+ break;
+ case NativeType.Func:
+ goto default; // ??
+ case NativeType.Array:
+ ArrayMarshalInfo ami = (ArrayMarshalInfo)marshalInfo;
+ if (ami == null)
+ goto default;
+ if (ami.ElementType != NativeType.Max)
+ WriteNativeType(ami.ElementType);
+ output.Write('[');
+ if (ami.SizeParameterMultiplier == 0) {
+ output.Write(ami.Size.ToString());
+ } else {
+ if (ami.Size >= 0)
+ output.Write(ami.Size.ToString());
+ output.Write(" + ");
+ output.Write(ami.SizeParameterIndex.ToString());
+ }
+ output.Write(']');
+ break;
+ case NativeType.Currency:
+ output.Write("currency");
+ break;
+ case NativeType.BStr:
+ output.Write("bstr");
+ break;
+ case NativeType.LPWStr:
+ output.Write("lpwstr");
+ break;
+ case NativeType.LPTStr:
+ output.Write("lptstr");
+ break;
+ case NativeType.FixedSysString:
+ output.Write("fixed sysstring[{0}]", ((FixedSysStringMarshalInfo)marshalInfo).Size);
+ break;
+ case NativeType.IUnknown:
+ output.Write("iunknown");
+ break;
+ case NativeType.IDispatch:
+ output.Write("idispatch");
+ break;
+ case NativeType.Struct:
+ output.Write("struct");
+ break;
+ case NativeType.IntF:
+ output.Write("interface");
+ break;
+ case NativeType.SafeArray:
+ output.Write("safearray ");
+ SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo;
+ if (sami != null) {
+ switch (sami.ElementType) {
+ case VariantType.None:
+ break;
+ case VariantType.I2:
+ output.Write("int16");
+ break;
+ case VariantType.I4:
+ output.Write("int32");
+ break;
+ case VariantType.R4:
+ output.Write("float32");
+ break;
+ case VariantType.R8:
+ output.Write("float64");
+ break;
+ case VariantType.CY:
+ output.Write("currency");
+ break;
+ case VariantType.Date:
+ output.Write("date");
+ break;
+ case VariantType.BStr:
+ output.Write("bstr");
+ break;
+ case VariantType.Dispatch:
+ output.Write("idispatch");
+ break;
+ case VariantType.Error:
+ output.Write("error");
+ break;
+ case VariantType.Bool:
+ output.Write("bool");
+ break;
+ case VariantType.Variant:
+ output.Write("variant");
+ break;
+ case VariantType.Unknown:
+ output.Write("iunknown");
+ break;
+ case VariantType.Decimal:
+ output.Write("decimal");
+ break;
+ case VariantType.I1:
+ output.Write("int8");
+ break;
+ case VariantType.UI1:
+ output.Write("unsigned int8");
+ break;
+ case VariantType.UI2:
+ output.Write("unsigned int16");
+ break;
+ case VariantType.UI4:
+ output.Write("unsigned int32");
+ break;
+ case VariantType.Int:
+ output.Write("int");
+ break;
+ case VariantType.UInt:
+ output.Write("unsigned int");
+ break;
+ default:
+ output.Write(sami.ElementType.ToString());
+ break;
+ }
+ }
+ break;
+ case NativeType.FixedArray:
+ output.Write("fixed array");
+ FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo;
+ if (fami != null) {
+ output.Write("[{0}]", fami.Size);
+ if (fami.ElementType != NativeType.None) {
+ output.Write(' ');
+ WriteNativeType(fami.ElementType);
+ }
+ }
+ break;
+ case NativeType.ByValStr:
+ output.Write("byvalstr");
+ break;
+ case NativeType.ANSIBStr:
+ output.Write("ansi bstr");
+ break;
+ case NativeType.TBStr:
+ output.Write("tbstr");
+ break;
+ case NativeType.VariantBool:
+ output.Write("variant bool");
+ break;
+ case NativeType.ASAny:
+ output.Write("as any");
+ break;
+ case NativeType.LPStruct:
+ output.Write("lpstruct");
+ break;
+ case NativeType.CustomMarshaler:
+ CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo;
+ if (cmi == null)
+ goto default;
+ output.Write("custom(\"{0}\", \"{1}\"",
+ NRefactory.CSharp.TextWriterTokenWriter.ConvertString(cmi.ManagedType.FullName),
+ NRefactory.CSharp.TextWriterTokenWriter.ConvertString(cmi.Cookie));
+ if (cmi.Guid != Guid.Empty || !string.IsNullOrEmpty(cmi.UnmanagedType)) {
+ output.Write(", \"{0}\", \"{1}\"", cmi.Guid.ToString(), NRefactory.CSharp.TextWriterTokenWriter.ConvertString(cmi.UnmanagedType));
+ }
+ output.Write(')');
+ break;
+ case NativeType.Error:
+ output.Write("error");
+ break;
+ default:
+ output.Write(nativeType.ToString());
+ break;
+ }
+ }
+ #endregion
+
+ void WriteParameters(Collection<ParameterDefinition> parameters)
+ {
+ for (int i = 0; i < parameters.Count; i++) {
+ var p = parameters[i];
+ if (p.IsIn)
+ output.Write("[in] ");
+ if (p.IsOut)
+ output.Write("[out] ");
+ if (p.IsOptional)
+ output.Write("[opt] ");
+ p.ParameterType.WriteTo(output);
+ output.Write(' ');
+ if (p.HasMarshalInfo) {
+ WriteMarshalInfo(p.MarshalInfo);
+ }
+ output.WriteDefinition(DisassemblerHelpers.Escape(p.Name), p);
+ if (i < parameters.Count - 1)
+ output.Write(',');
+ output.WriteLine();
+ }
+ }
+
+ bool HasParameterAttributes(ParameterDefinition p)
+ {
+ return p.HasConstant || p.HasCustomAttributes;
+ }
+
+ void WriteParameterAttributes(ParameterDefinition p)
+ {
+ if (!HasParameterAttributes(p))
+ return;
+ output.Write(".param [{0}]", p.Index + 1);
+ if (p.HasConstant) {
+ output.Write(" = ");
+ WriteConstant(p.Constant);
+ }
+ output.WriteLine();
+ WriteAttributes(p.CustomAttributes);
+ }
+
+ void WriteConstant(object constant)
+ {
+ if (constant == null) {
+ output.Write("nullref");
+ } else {
+ string typeName = DisassemblerHelpers.PrimitiveTypeName(constant.GetType().FullName);
+ if (typeName != null && typeName != "string") {
+ output.Write(typeName);
+ output.Write('(');
+ float? cf = constant as float?;
+ double? cd = constant as double?;
+ if (cf.HasValue && (float.IsNaN(cf.Value) || float.IsInfinity(cf.Value))) {
+ output.Write("0x{0:x8}", BitConverter.ToInt32(BitConverter.GetBytes(cf.Value), 0));
+ } else if (cd.HasValue && (double.IsNaN(cd.Value) || double.IsInfinity(cd.Value))) {
+ output.Write("0x{0:x16}", BitConverter.DoubleToInt64Bits(cd.Value));
+ } else {
+ DisassemblerHelpers.WriteOperand(output, constant);
+ }
+ output.Write(')');
+ } else {
+ DisassemblerHelpers.WriteOperand(output, constant);
+ }
+ }
+ }
+ #endregion
+
+ #region Disassemble Field
+ EnumNameCollection<FieldAttributes> fieldVisibility = new EnumNameCollection<FieldAttributes>() {
+ { FieldAttributes.Private, "private" },
+ { FieldAttributes.FamANDAssem, "famandassem" },
+ { FieldAttributes.Assembly, "assembly" },
+ { FieldAttributes.Family, "family" },
+ { FieldAttributes.FamORAssem, "famorassem" },
+ { FieldAttributes.Public, "public" },
+ };
+
+ EnumNameCollection<FieldAttributes> fieldAttributes = new EnumNameCollection<FieldAttributes>() {
+ { FieldAttributes.Static, "static" },
+ { FieldAttributes.Literal, "literal" },
+ { FieldAttributes.InitOnly, "initonly" },
+ { FieldAttributes.SpecialName, "specialname" },
+ { FieldAttributes.RTSpecialName, "rtspecialname" },
+ { FieldAttributes.NotSerialized, "notserialized" },
+ };
+
+ public void DisassembleField(FieldDefinition field)
+ {
+ output.WriteDefinition(".field ", field);
+ if (field.HasLayoutInfo) {
+ output.Write("[" + field.Offset + "] ");
+ }
+ WriteEnum(field.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility);
+ const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA;
+ WriteFlags(field.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), fieldAttributes);
+ if (field.HasMarshalInfo) {
+ WriteMarshalInfo(field.MarshalInfo);
+ }
+ field.FieldType.WriteTo(output);
+ output.Write(' ');
+ output.Write(DisassemblerHelpers.Escape(field.Name));
+ if ((field.Attributes & FieldAttributes.HasFieldRVA) == FieldAttributes.HasFieldRVA) {
+ output.Write(" at I_{0:x8}", field.RVA);
+ }
+ if (field.HasConstant) {
+ output.Write(" = ");
+ WriteConstant(field.Constant);
+ }
+ output.WriteLine();
+ if (field.HasCustomAttributes) {
+ output.MarkFoldStart();
+ WriteAttributes(field.CustomAttributes);
+ output.MarkFoldEnd();
+ }
+ }
+ #endregion
+
+ #region Disassemble Property
+ EnumNameCollection<PropertyAttributes> propertyAttributes = new EnumNameCollection<PropertyAttributes>() {
+ { PropertyAttributes.SpecialName, "specialname" },
+ { PropertyAttributes.RTSpecialName, "rtspecialname" },
+ { PropertyAttributes.HasDefault, "hasdefault" },
+ };
+
+ public void DisassembleProperty(PropertyDefinition property)
+ {
+ // set current member
+ currentMember = property;
+
+ output.WriteDefinition(".property ", property);
+ WriteFlags(property.Attributes, propertyAttributes);
+ if (property.HasThis)
+ output.Write("instance ");
+ property.PropertyType.WriteTo(output);
+ output.Write(' ');
+ output.Write(DisassemblerHelpers.Escape(property.Name));
+
+ output.Write("(");
+ if (property.HasParameters) {
+ output.WriteLine();
+ output.Indent();
+ WriteParameters(property.Parameters);
+ output.Unindent();
+ }
+ output.Write(")");
+
+ OpenBlock(false);
+ WriteAttributes(property.CustomAttributes);
+ WriteNestedMethod(".get", property.GetMethod);
+ WriteNestedMethod(".set", property.SetMethod);
+
+ foreach (var method in property.OtherMethods) {
+ WriteNestedMethod(".other", method);
+ }
+ CloseBlock();
+ }
+
+ void WriteNestedMethod(string keyword, MethodDefinition method)
+ {
+ if (method == null)
+ return;
+
+ output.Write(keyword);
+ output.Write(' ');
+ method.WriteTo(output);
+ output.WriteLine();
+ }
+ #endregion
+
+ #region Disassemble Event
+ EnumNameCollection<EventAttributes> eventAttributes = new EnumNameCollection<EventAttributes>() {
+ { EventAttributes.SpecialName, "specialname" },
+ { EventAttributes.RTSpecialName, "rtspecialname" },
+ };
+
+ public void DisassembleEvent(EventDefinition ev)
+ {
+ // set current member
+ currentMember = ev;
+
+ output.WriteDefinition(".event ", ev);
+ WriteFlags(ev.Attributes, eventAttributes);
+ ev.EventType.WriteTo(output, ILNameSyntax.TypeName);
+ output.Write(' ');
+ output.Write(DisassemblerHelpers.Escape(ev.Name));
+ OpenBlock(false);
+ WriteAttributes(ev.CustomAttributes);
+ WriteNestedMethod(".addon", ev.AddMethod);
+ WriteNestedMethod(".removeon", ev.RemoveMethod);
+ WriteNestedMethod(".fire", ev.InvokeMethod);
+ foreach (var method in ev.OtherMethods) {
+ WriteNestedMethod(".other", method);
+ }
+ CloseBlock();
+ }
+ #endregion
+
+ #region Disassemble Type
+ EnumNameCollection<TypeAttributes> typeVisibility = new EnumNameCollection<TypeAttributes>() {
+ { TypeAttributes.Public, "public" },
+ { TypeAttributes.NotPublic, "private" },
+ { TypeAttributes.NestedPublic, "nested public" },
+ { TypeAttributes.NestedPrivate, "nested private" },
+ { TypeAttributes.NestedAssembly, "nested assembly" },
+ { TypeAttributes.NestedFamily, "nested family" },
+ { TypeAttributes.NestedFamANDAssem, "nested famandassem" },
+ { TypeAttributes.NestedFamORAssem, "nested famorassem" },
+ };
+
+ EnumNameCollection<TypeAttributes> typeLayout = new EnumNameCollection<TypeAttributes>() {
+ { TypeAttributes.AutoLayout, "auto" },
+ { TypeAttributes.SequentialLayout, "sequential" },
+ { TypeAttributes.ExplicitLayout, "explicit" },
+ };
+
+ EnumNameCollection<TypeAttributes> typeStringFormat = new EnumNameCollection<TypeAttributes>() {
+ { TypeAttributes.AutoClass, "auto" },
+ { TypeAttributes.AnsiClass, "ansi" },
+ { TypeAttributes.UnicodeClass, "unicode" },
+ };
+
+ EnumNameCollection<TypeAttributes> typeAttributes = new EnumNameCollection<TypeAttributes>() {
+ { TypeAttributes.Abstract, "abstract" },
+ { TypeAttributes.Sealed, "sealed" },
+ { TypeAttributes.SpecialName, "specialname" },
+ { TypeAttributes.Import, "import" },
+ { TypeAttributes.Serializable, "serializable" },
+ { TypeAttributes.WindowsRuntime, "windowsruntime" },
+ { TypeAttributes.BeforeFieldInit, "beforefieldinit" },
+ { TypeAttributes.HasSecurity, null },
+ };
+
+ public void DisassembleType(TypeDefinition type)
+ {
+ // start writing IL
+ output.WriteDefinition(".class ", type);
+
+ if ((type.Attributes & TypeAttributes.ClassSemanticMask) == TypeAttributes.Interface)
+ output.Write("interface ");
+ WriteEnum(type.Attributes & TypeAttributes.VisibilityMask, typeVisibility);
+ WriteEnum(type.Attributes & TypeAttributes.LayoutMask, typeLayout);
+ WriteEnum(type.Attributes & TypeAttributes.StringFormatMask, typeStringFormat);
+ const TypeAttributes masks = TypeAttributes.ClassSemanticMask | TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.StringFormatMask;
+ WriteFlags(type.Attributes & ~masks, typeAttributes);
+
+ output.Write(DisassemblerHelpers.Escape(type.DeclaringType != null ? type.Name : type.FullName));
+ WriteTypeParameters(output, type);
+ output.MarkFoldStart(defaultCollapsed: isInType);
+ output.WriteLine();
+
+ if (type.BaseType != null) {
+ output.Indent();
+ output.Write("extends ");
+ type.BaseType.WriteTo(output, ILNameSyntax.TypeName);
+ output.WriteLine();
+ output.Unindent();
+ }
+ if (type.HasInterfaces) {
+ output.Indent();
+ for (int index = 0; index < type.Interfaces.Count; index++) {
+ if (index > 0)
+ output.WriteLine(",");
+ if (index == 0)
+ output.Write("implements ");
+ else
+ output.Write(" ");
+ type.Interfaces[index].WriteTo(output, ILNameSyntax.TypeName);
+ }
+ output.WriteLine();
+ output.Unindent();
+ }
+
+ output.WriteLine("{");
+ output.Indent();
+ bool oldIsInType = isInType;
+ isInType = true;
+ WriteAttributes(type.CustomAttributes);
+ WriteSecurityDeclarations(type);
+ if (type.HasLayoutInfo) {
+ output.WriteLine(".pack {0}", type.PackingSize);
+ output.WriteLine(".size {0}", type.ClassSize);
+ output.WriteLine();
+ }
+ if (type.HasNestedTypes) {
+ output.WriteLine("// Nested Types");
+ foreach (var nestedType in type.NestedTypes) {
+ cancellationToken.ThrowIfCancellationRequested();
+ DisassembleType(nestedType);
+ output.WriteLine();
+ }
+ output.WriteLine();
+ }
+ if (type.HasFields) {
+ output.WriteLine("// Fields");
+ foreach (var field in type.Fields) {
+ cancellationToken.ThrowIfCancellationRequested();
+ DisassembleField(field);
+ }
+ output.WriteLine();
+ }
+ if (type.HasMethods) {
+ output.WriteLine("// Methods");
+ foreach (var m in type.Methods) {
+ cancellationToken.ThrowIfCancellationRequested();
+ DisassembleMethod(m);
+ output.WriteLine();
+ }
+ }
+ if (type.HasEvents) {
+ output.WriteLine("// Events");
+ foreach (var ev in type.Events) {
+ cancellationToken.ThrowIfCancellationRequested();
+ DisassembleEvent(ev);
+ output.WriteLine();
+ }
+ output.WriteLine();
+ }
+ if (type.HasProperties) {
+ output.WriteLine("// Properties");
+ foreach (var prop in type.Properties) {
+ cancellationToken.ThrowIfCancellationRequested();
+ DisassembleProperty(prop);
+ }
+ output.WriteLine();
+ }
+ CloseBlock("end of class " + (type.DeclaringType != null ? type.Name : type.FullName));
+ isInType = oldIsInType;
+ }
+
+ void WriteTypeParameters(ITextOutput output, IGenericParameterProvider p)
+ {
+ if (p.HasGenericParameters) {
+ output.Write('<');
+ for (int i = 0; i < p.GenericParameters.Count; i++) {
+ if (i > 0)
+ output.Write(", ");
+ GenericParameter gp = p.GenericParameters[i];
+ if (gp.HasReferenceTypeConstraint) {
+ output.Write("class ");
+ } else if (gp.HasNotNullableValueTypeConstraint) {
+ output.Write("valuetype ");
+ }
+ if (gp.HasDefaultConstructorConstraint) {
+ output.Write(".ctor ");
+ }
+ if (gp.HasConstraints) {
+ output.Write('(');
+ for (int j = 0; j < gp.Constraints.Count; j++) {
+ if (j > 0)
+ output.Write(", ");
+ gp.Constraints[j].WriteTo(output, ILNameSyntax.TypeName);
+ }
+ output.Write(") ");
+ }
+ if (gp.IsContravariant) {
+ output.Write('-');
+ } else if (gp.IsCovariant) {
+ output.Write('+');
+ }
+ output.Write(DisassemblerHelpers.Escape(gp.Name));
+ }
+ output.Write('>');
+ }
+ }
+ #endregion
+
+ #region Helper methods
+ void WriteAttributes(Collection<CustomAttribute> attributes)
+ {
+ foreach (CustomAttribute a in attributes) {
+ output.Write(".custom ");
+ a.Constructor.WriteTo(output);
+ byte[] blob = a.GetBlob();
+ if (blob != null) {
+ output.Write(" = ");
+ WriteBlob(blob);
+ }
+ output.WriteLine();
+ }
+ }
+
+ void WriteBlob(byte[] blob)
+ {
+ output.Write("(");
+ output.Indent();
+
+ for (int i = 0; i < blob.Length; i++) {
+ if (i % 16 == 0 && i < blob.Length - 1) {
+ output.WriteLine();
+ } else {
+ output.Write(' ');
+ }
+ output.Write(blob[i].ToString("x2"));
+ }
+
+ output.WriteLine();
+ output.Unindent();
+ output.Write(")");
+ }
+
+ void OpenBlock(bool defaultCollapsed)
+ {
+ output.MarkFoldStart(defaultCollapsed: defaultCollapsed);
+ output.WriteLine();
+ output.WriteLine("{");
+ output.Indent();
+ }
+
+ void CloseBlock(string comment = null)
+ {
+ output.Unindent();
+ output.Write("}");
+ if (comment != null)
+ output.Write(" // " + comment);
+ output.MarkFoldEnd();
+ output.WriteLine();
+ }
+
+ void WriteFlags<T>(T flags, EnumNameCollection<T> flagNames) where T : struct
+ {
+ long val = Convert.ToInt64(flags);
+ long tested = 0;
+ foreach (var pair in flagNames) {
+ tested |= pair.Key;
+ if ((val & pair.Key) != 0 && pair.Value != null) {
+ output.Write(pair.Value);
+ output.Write(' ');
+ }
+ }
+ if ((val & ~tested) != 0)
+ output.Write("flag({0:x4}) ", val & ~tested);
+ }
+
+ void WriteEnum<T>(T enumValue, EnumNameCollection<T> enumNames) where T : struct
+ {
+ long val = Convert.ToInt64(enumValue);
+ foreach (var pair in enumNames) {
+ if (pair.Key == val) {
+ if (pair.Value != null) {
+ output.Write(pair.Value);
+ output.Write(' ');
+ }
+ return;
+ }
+ }
+ if (val != 0) {
+ output.Write("flag({0:x4})", val);
+ output.Write(' ');
+ }
+
+ }
+
+ sealed class EnumNameCollection<T> : IEnumerable<KeyValuePair<long, string>> where T : struct
+ {
+ List<KeyValuePair<long, string>> names = new List<KeyValuePair<long, string>>();
+
+ public void Add(T flag, string name)
+ {
+ names.Add(new KeyValuePair<long, string>(Convert.ToInt64(flag), name));
+ }
+
+ public IEnumerator<KeyValuePair<long, string>> GetEnumerator()
+ {
+ return names.GetEnumerator();
+ }
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return names.GetEnumerator();
+ }
+ }
+ #endregion
+
+ public void DisassembleNamespace(string nameSpace, IEnumerable<TypeDefinition> types)
+ {
+ if (!string.IsNullOrEmpty(nameSpace)) {
+ output.Write(".namespace " + DisassemblerHelpers.Escape(nameSpace));
+ OpenBlock(false);
+ }
+ bool oldIsInType = isInType;
+ isInType = true;
+ foreach (TypeDefinition td in types) {
+ cancellationToken.ThrowIfCancellationRequested();
+ DisassembleType(td);
+ output.WriteLine();
+ }
+ if (!string.IsNullOrEmpty(nameSpace)) {
+ CloseBlock();
+ isInType = oldIsInType;
+ }
+ }
+
+ public void WriteAssemblyHeader(AssemblyDefinition asm)
+ {
+ output.Write(".assembly ");
+ if (asm.Name.IsWindowsRuntime)
+ output.Write("windowsruntime ");
+ output.Write(DisassemblerHelpers.Escape(asm.Name.Name));
+ OpenBlock(false);
+ WriteAttributes(asm.CustomAttributes);
+ WriteSecurityDeclarations(asm);
+ if (asm.Name.PublicKey != null && asm.Name.PublicKey.Length > 0) {
+ output.Write(".publickey = ");
+ WriteBlob(asm.Name.PublicKey);
+ output.WriteLine();
+ }
+ if (asm.Name.HashAlgorithm != AssemblyHashAlgorithm.None) {
+ output.Write(".hash algorithm 0x{0:x8}", (int)asm.Name.HashAlgorithm);
+ if (asm.Name.HashAlgorithm == AssemblyHashAlgorithm.SHA1)
+ output.Write(" // SHA1");
+ output.WriteLine();
+ }
+ Version v = asm.Name.Version;
+ if (v != null) {
+ output.WriteLine(".ver {0}:{1}:{2}:{3}", v.Major, v.Minor, v.Build, v.Revision);
+ }
+ CloseBlock();
+ }
+
+ public void WriteAssemblyReferences(ModuleDefinition module)
+ {
+ foreach (var mref in module.ModuleReferences) {
+ output.WriteLine(".module extern {0}", DisassemblerHelpers.Escape(mref.Name));
+ }
+ foreach (var aref in module.AssemblyReferences) {
+ output.Write(".assembly extern ");
+ if (aref.IsWindowsRuntime)
+ output.Write("windowsruntime ");
+ output.Write(DisassemblerHelpers.Escape(aref.Name));
+ OpenBlock(false);
+ if (aref.PublicKeyToken != null) {
+ output.Write(".publickeytoken = ");
+ WriteBlob(aref.PublicKeyToken);
+ output.WriteLine();
+ }
+ if (aref.Version != null) {
+ output.WriteLine(".ver {0}:{1}:{2}:{3}", aref.Version.Major, aref.Version.Minor, aref.Version.Build, aref.Version.Revision);
+ }
+ CloseBlock();
+ }
+ }
+
+ public void WriteModuleHeader(ModuleDefinition module)
+ {
+ if (module.HasExportedTypes) {
+ foreach (ExportedType exportedType in module.ExportedTypes) {
+ output.Write(".class extern ");
+ if (exportedType.IsForwarder)
+ output.Write("forwarder ");
+ output.Write(exportedType.DeclaringType != null ? exportedType.Name : exportedType.FullName);
+ OpenBlock(false);
+ if (exportedType.DeclaringType != null)
+ output.WriteLine(".class extern {0}", DisassemblerHelpers.Escape(exportedType.DeclaringType.FullName));
+ else
+ output.WriteLine(".assembly extern {0}", DisassemblerHelpers.Escape(exportedType.Scope.Name));
+ CloseBlock();
+ }
+ }
+
+ output.WriteLine(".module {0}", module.Name);
+ output.WriteLine("// MVID: {0}", module.Mvid.ToString("B").ToUpperInvariant());
+ // TODO: imagebase, file alignment, stackreserve, subsystem
+ output.WriteLine(".corflags 0x{0:x} // {1}", module.Attributes, module.Attributes.ToString());
+
+ WriteAttributes(module.CustomAttributes);
+ }
+
+ public void WriteModuleContents(ModuleDefinition module)
+ {
+ foreach (TypeDefinition td in module.Types) {
+ DisassembleType(td);
+ output.WriteLine();
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs
new file mode 100644
index 00000000..98bd4399
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs
@@ -0,0 +1,78 @@
+// 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;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ /// <summary>
+ /// Describes the type of a control flow egde.
+ /// </summary>
+ public enum JumpType
+ {
+ /// <summary>
+ /// A regular control flow edge.
+ /// </summary>
+ Normal,
+ /// <summary>
+ /// Jump to exception handler (an exception occurred)
+ /// </summary>
+ JumpToExceptionHandler,
+ /// <summary>
+ /// Jump from try block to leave target:
+ /// This is not a real jump, as the finally handler is executed first!
+ /// </summary>
+ LeaveTry,
+ /// <summary>
+ /// Jump at endfinally (to any of the potential leave targets).
+ /// For any leave-instruction, control flow enters the finally block - the edge to the leave target (LeaveTry) is not a real control flow edge.
+ /// EndFinally edges are inserted at the end of the finally block, jumping to any of the targets of the leave instruction.
+ /// This edge type is only used when copying of finally blocks is disabled (with copying, a normal deterministic edge is used at each copy of the endfinally node).
+ /// </summary>
+ EndFinally
+ }
+
+ /// <summary>
+ /// Represents an edge in the control flow graph, pointing from Source to Target.
+ /// </summary>
+ public sealed class ControlFlowEdge
+ {
+ public readonly ControlFlowNode Source;
+ public readonly ControlFlowNode Target;
+ public readonly JumpType Type;
+
+ public ControlFlowEdge(ControlFlowNode source, ControlFlowNode target, JumpType type)
+ {
+ Source = source;
+ Target = target;
+ Type = type;
+ }
+
+ public override string ToString()
+ {
+ switch (Type) {
+ case JumpType.Normal:
+ return "#" + Target.BlockIndex;
+ case JumpType.JumpToExceptionHandler:
+ return "e:#" + Target.BlockIndex;
+ default:
+ return Type.ToString() + ":#" + Target.BlockIndex;
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs
new file mode 100644
index 00000000..7cc815a6
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs
@@ -0,0 +1,191 @@
+// 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.Diagnostics;
+using System.Linq;
+using System.Threading;
+
+using ICSharpCode.NRefactory.Utils;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ /// <summary>
+ /// Contains the control flow graph.
+ /// </summary>
+ /// <remarks>Use ControlFlowGraph builder to create instances of the ControlFlowGraph.</remarks>
+ public sealed class ControlFlowGraph
+ {
+ readonly ReadOnlyCollection<ControlFlowNode> nodes;
+
+ public ControlFlowNode EntryPoint {
+ get { return nodes[0]; }
+ }
+
+ public ControlFlowNode RegularExit {
+ get { return nodes[1]; }
+ }
+
+ public ControlFlowNode ExceptionalExit {
+ get { return nodes[2]; }
+ }
+
+ public ReadOnlyCollection<ControlFlowNode> Nodes {
+ get { return nodes; }
+ }
+
+ internal ControlFlowGraph(ControlFlowNode[] nodes)
+ {
+ this.nodes = new ReadOnlyCollection<ControlFlowNode>(nodes);
+ Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint);
+ Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit);
+ Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit);
+ }
+
+ public GraphVizGraph ExportGraph()
+ {
+ GraphVizGraph graph = new GraphVizGraph();
+ foreach (ControlFlowNode node in nodes) {
+ graph.AddNode(new GraphVizNode(node.BlockIndex) { label = node.ToString(), shape = "box" });
+ }
+ foreach (ControlFlowNode node in nodes) {
+ foreach (ControlFlowEdge edge in node.Outgoing) {
+ GraphVizEdge e = new GraphVizEdge(edge.Source.BlockIndex, edge.Target.BlockIndex);
+ switch (edge.Type) {
+ case JumpType.Normal:
+ break;
+ case JumpType.LeaveTry:
+ case JumpType.EndFinally:
+ e.color = "red";
+ break;
+ default:
+ e.color = "gray";
+ //e.constraint = false;
+ break;
+ }
+ graph.AddEdge(e);
+ }
+ if (node.ImmediateDominator != null) {
+ graph.AddEdge(new GraphVizEdge(node.ImmediateDominator.BlockIndex, node.BlockIndex) { color = "green", constraint = false });
+ }
+ }
+ return graph;
+ }
+
+ /// <summary>
+ /// Resets "Visited" to false for all nodes in this graph.
+ /// </summary>
+ public void ResetVisited()
+ {
+ foreach (ControlFlowNode node in nodes) {
+ node.Visited = false;
+ }
+ }
+
+ /// <summary>
+ /// Computes the dominator tree.
+ /// </summary>
+ public void ComputeDominance(CancellationToken cancellationToken = default(CancellationToken))
+ {
+ // A Simple, Fast Dominance Algorithm
+ // Keith D. Cooper, Timothy J. Harvey and Ken Kennedy
+
+ EntryPoint.ImmediateDominator = EntryPoint;
+ bool changed = true;
+ while (changed) {
+ changed = false;
+ ResetVisited();
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // for all nodes b except the entry point
+ EntryPoint.TraversePreOrder(
+ b => b.Successors,
+ b => {
+ if (b != EntryPoint) {
+ ControlFlowNode newIdom = b.Predecessors.First(block => block.Visited && block != b);
+ // for all other predecessors p of b
+ foreach (ControlFlowNode p in b.Predecessors) {
+ if (p != b && p.ImmediateDominator != null) {
+ newIdom = FindCommonDominator(p, newIdom);
+ }
+ }
+ if (b.ImmediateDominator != newIdom) {
+ b.ImmediateDominator = newIdom;
+ changed = true;
+ }
+ }
+ });
+ }
+ EntryPoint.ImmediateDominator = null;
+ foreach (ControlFlowNode node in nodes) {
+ if (node.ImmediateDominator != null)
+ node.ImmediateDominator.DominatorTreeChildren.Add(node);
+ }
+ }
+
+ static ControlFlowNode FindCommonDominator(ControlFlowNode b1, ControlFlowNode b2)
+ {
+ // Here we could use the postorder numbers to get rid of the hashset, see "A Simple, Fast Dominance Algorithm"
+ HashSet<ControlFlowNode> path1 = new HashSet<ControlFlowNode>();
+ while (b1 != null && path1.Add(b1))
+ b1 = b1.ImmediateDominator;
+ while (b2 != null) {
+ if (path1.Contains(b2))
+ return b2;
+ else
+ b2 = b2.ImmediateDominator;
+ }
+ throw new Exception("No common dominator found!");
+ }
+
+ /// <summary>
+ /// Computes dominance frontiers.
+ /// This method requires that the dominator tree is already computed!
+ /// </summary>
+ public void ComputeDominanceFrontier()
+ {
+ ResetVisited();
+
+ EntryPoint.TraversePostOrder(
+ b => b.DominatorTreeChildren,
+ n => {
+ //logger.WriteLine("Calculating dominance frontier for " + n.Name);
+ n.DominanceFrontier = new HashSet<ControlFlowNode>();
+ // DF_local computation
+ foreach (ControlFlowNode succ in n.Successors) {
+ if (succ.ImmediateDominator != n) {
+ //logger.WriteLine(" local: " + succ.Name);
+ n.DominanceFrontier.Add(succ);
+ }
+ }
+ // DF_up computation
+ foreach (ControlFlowNode child in n.DominatorTreeChildren) {
+ foreach (ControlFlowNode p in child.DominanceFrontier) {
+ if (p.ImmediateDominator != n) {
+ //logger.WriteLine(" DF_up: " + p.Name + " (child=" + child.Name);
+ n.DominanceFrontier.Add(p);
+ }
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
new file mode 100644
index 00000000..7570884a
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
@@ -0,0 +1,439 @@
+// 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 Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ /// <summary>
+ /// Constructs the Control Flow Graph from a Cecil method body.
+ /// </summary>
+ public sealed class ControlFlowGraphBuilder
+ {
+ public static ControlFlowGraph Build(MethodBody methodBody)
+ {
+ return new ControlFlowGraphBuilder(methodBody).Build();
+ }
+
+ // This option controls how finally blocks are handled:
+ // false means that the endfinally instruction will jump to any of the leave targets (EndFinally edge type).
+ // true means that a copy of the whole finally block is created for each leave target. In this case, each endfinally node will be connected with the leave
+ // target using a normal edge.
+ bool copyFinallyBlocks = false;
+
+ MethodBody methodBody;
+ int[] offsets; // array index = instruction index; value = IL offset
+ bool[] hasIncomingJumps; // array index = instruction index
+ List<ControlFlowNode> nodes = new List<ControlFlowNode>();
+ ControlFlowNode entryPoint;
+ ControlFlowNode regularExit;
+ ControlFlowNode exceptionalExit;
+
+ ControlFlowGraphBuilder(MethodBody methodBody)
+ {
+ this.methodBody = methodBody;
+ offsets = methodBody.Instructions.Select(i => i.Offset).ToArray();
+ hasIncomingJumps = new bool[methodBody.Instructions.Count];
+
+ entryPoint = new ControlFlowNode(0, 0, ControlFlowNodeType.EntryPoint);
+ nodes.Add(entryPoint);
+ regularExit = new ControlFlowNode(1, -1, ControlFlowNodeType.RegularExit);
+ nodes.Add(regularExit);
+ exceptionalExit = new ControlFlowNode(2, -1, ControlFlowNodeType.ExceptionalExit);
+ nodes.Add(exceptionalExit);
+ Debug.Assert(nodes.Count == 3);
+ }
+
+ /// <summary>
+ /// Determines the index of the instruction (for use with the hasIncomingJumps array)
+ /// </summary>
+ int GetInstructionIndex(Instruction inst)
+ {
+ int index = Array.BinarySearch(offsets, inst.Offset);
+ Debug.Assert(index >= 0);
+ return index;
+ }
+
+ /// <summary>
+ /// Builds the ControlFlowGraph.
+ /// </summary>
+ public ControlFlowGraph Build()
+ {
+ CalculateHasIncomingJumps();
+ CreateNodes();
+ CreateRegularControlFlow();
+ CreateExceptionalControlFlow();
+ if (copyFinallyBlocks)
+ CopyFinallyBlocksIntoLeaveEdges();
+ else
+ TransformLeaveEdges();
+ return new ControlFlowGraph(nodes.ToArray());
+ }
+
+ #region Step 1: calculate which instructions are the targets of jump instructions.
+ void CalculateHasIncomingJumps()
+ {
+ foreach (Instruction inst in methodBody.Instructions) {
+ if (inst.OpCode.OperandType == OperandType.InlineBrTarget || inst.OpCode.OperandType == OperandType.ShortInlineBrTarget) {
+ hasIncomingJumps[GetInstructionIndex((Instruction)inst.Operand)] = true;
+ } else if (inst.OpCode.OperandType == OperandType.InlineSwitch) {
+ foreach (Instruction i in (Instruction[])inst.Operand)
+ hasIncomingJumps[GetInstructionIndex(i)] = true;
+ }
+ }
+ foreach (ExceptionHandler eh in methodBody.ExceptionHandlers) {
+ if (eh.FilterStart != null) {
+ hasIncomingJumps[GetInstructionIndex(eh.FilterStart)] = true;
+ }
+ hasIncomingJumps[GetInstructionIndex(eh.HandlerStart)] = true;
+ }
+ }
+ #endregion
+
+ #region Step 2: create nodes
+ void CreateNodes()
+ {
+ // Step 2a: find basic blocks and create nodes for them
+ for (int i = 0; i < methodBody.Instructions.Count; i++) {
+ Instruction blockStart = methodBody.Instructions[i];
+ ExceptionHandler blockStartEH = FindInnermostExceptionHandler(blockStart.Offset);
+ // try and see how big we can make that block:
+ for (; i + 1 < methodBody.Instructions.Count; i++) {
+ Instruction inst = methodBody.Instructions[i];
+ if (IsBranch(inst.OpCode) || CanThrowException(inst.OpCode))
+ break;
+ if (hasIncomingJumps[i + 1])
+ break;
+ if (inst.Next != null) {
+ // ensure that blocks never contain instructions from different try blocks
+ ExceptionHandler instEH = FindInnermostExceptionHandler(inst.Next.Offset);
+ if (instEH != blockStartEH)
+ break;
+ }
+ }
+
+ nodes.Add(new ControlFlowNode(nodes.Count, blockStart, methodBody.Instructions[i]));
+ }
+ // Step 2b: Create special nodes for the exception handling constructs
+ foreach (ExceptionHandler handler in methodBody.ExceptionHandlers) {
+ if (handler.HandlerType == ExceptionHandlerType.Filter)
+ throw new NotSupportedException();
+ ControlFlowNode endFinallyOrFaultNode = null;
+ if (handler.HandlerType == ExceptionHandlerType.Finally || handler.HandlerType == ExceptionHandlerType.Fault) {
+ endFinallyOrFaultNode = new ControlFlowNode(nodes.Count, handler.HandlerEnd.Offset, ControlFlowNodeType.EndFinallyOrFault);
+ nodes.Add(endFinallyOrFaultNode);
+ }
+ nodes.Add(new ControlFlowNode(nodes.Count, handler, endFinallyOrFaultNode));
+ }
+ }
+ #endregion
+
+ #region Step 3: create edges for the normal flow of control (assuming no exceptions thrown)
+ void CreateRegularControlFlow()
+ {
+ CreateEdge(entryPoint, methodBody.Instructions[0], JumpType.Normal);
+ foreach (ControlFlowNode node in nodes) {
+ if (node.End != null) {
+ // create normal edges from one instruction to the next
+ if (!OpCodeInfo.IsUnconditionalBranch(node.End.OpCode))
+ CreateEdge(node, node.End.Next, JumpType.Normal);
+
+ // create edges for branch instructions
+ if (node.End.OpCode.OperandType == OperandType.InlineBrTarget || node.End.OpCode.OperandType == OperandType.ShortInlineBrTarget) {
+ if (node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S) {
+ var handlerBlock = FindInnermostHandlerBlock(node.End.Offset);
+ if (handlerBlock.NodeType == ControlFlowNodeType.FinallyOrFaultHandler)
+ CreateEdge(node, (Instruction)node.End.Operand, JumpType.LeaveTry);
+ else
+ CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal);
+ } else {
+ CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal);
+ }
+ } else if (node.End.OpCode.OperandType == OperandType.InlineSwitch) {
+ foreach (Instruction i in (Instruction[])node.End.Operand)
+ CreateEdge(node, i, JumpType.Normal);
+ }
+
+ // create edges for return instructions
+ if (node.End.OpCode.FlowControl == FlowControl.Return) {
+ switch (node.End.OpCode.Code) {
+ case Code.Ret:
+ CreateEdge(node, regularExit, JumpType.Normal);
+ break;
+ case Code.Endfinally:
+ ControlFlowNode handlerBlock = FindInnermostHandlerBlock(node.End.Offset);
+ if (handlerBlock.EndFinallyOrFaultNode == null)
+ throw new InvalidProgramException("Found endfinally in block " + handlerBlock);
+ CreateEdge(node, handlerBlock.EndFinallyOrFaultNode, JumpType.Normal);
+ break;
+ default:
+ throw new NotSupportedException(node.End.OpCode.ToString());
+ }
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region Step 4: create edges for the exceptional control flow (from instructions that might throw, to the innermost containing exception handler)
+ void CreateExceptionalControlFlow()
+ {
+ foreach (ControlFlowNode node in nodes) {
+ if (node.End != null && CanThrowException(node.End.OpCode)) {
+ CreateEdge(node, FindInnermostExceptionHandlerNode(node.End.Offset), JumpType.JumpToExceptionHandler);
+ }
+ if (node.ExceptionHandler != null) {
+ if (node.EndFinallyOrFaultNode != null) {
+ // For Fault and Finally blocks, create edge from "EndFinally" to next exception handler.
+ // This represents the exception bubbling up after finally block was executed.
+ CreateEdge(node.EndFinallyOrFaultNode, FindParentExceptionHandlerNode(node), JumpType.JumpToExceptionHandler);
+ } else {
+ // For Catch blocks, create edge from "CatchHandler" block (at beginning) to next exception handler.
+ // This represents the exception bubbling up because it did not match the type of the catch block.
+ CreateEdge(node, FindParentExceptionHandlerNode(node), JumpType.JumpToExceptionHandler);
+ }
+ CreateEdge(node, node.ExceptionHandler.HandlerStart, JumpType.Normal);
+ }
+ }
+ }
+
+ ExceptionHandler FindInnermostExceptionHandler(int instructionOffsetInTryBlock)
+ {
+ foreach (ExceptionHandler h in methodBody.ExceptionHandlers) {
+ if (h.TryStart.Offset <= instructionOffsetInTryBlock && instructionOffsetInTryBlock < h.TryEnd.Offset) {
+ return h;
+ }
+ }
+ return null;
+ }
+
+ ControlFlowNode FindInnermostExceptionHandlerNode(int instructionOffsetInTryBlock)
+ {
+ ExceptionHandler h = FindInnermostExceptionHandler(instructionOffsetInTryBlock);
+ if (h != null)
+ return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null);
+ else
+ return exceptionalExit;
+ }
+
+ ControlFlowNode FindInnermostHandlerBlock(int instructionOffset)
+ {
+ foreach (ExceptionHandler h in methodBody.ExceptionHandlers) {
+ if (h.TryStart.Offset <= instructionOffset && instructionOffset < h.TryEnd.Offset
+ || h.HandlerStart.Offset <= instructionOffset && instructionOffset < h.HandlerEnd.Offset)
+ {
+ return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null);
+ }
+ }
+ return exceptionalExit;
+ }
+
+ ControlFlowNode FindParentExceptionHandlerNode(ControlFlowNode exceptionHandler)
+ {
+ Debug.Assert(exceptionHandler.NodeType == ControlFlowNodeType.CatchHandler
+ || exceptionHandler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
+ int offset = exceptionHandler.ExceptionHandler.TryStart.Offset;
+ for (int i = exceptionHandler.BlockIndex + 1; i < nodes.Count; i++) {
+ ExceptionHandler h = nodes[i].ExceptionHandler;
+ if (h != null && h.TryStart.Offset <= offset && offset < h.TryEnd.Offset)
+ return nodes[i];
+ }
+ return exceptionalExit;
+ }
+ #endregion
+
+ #region Step 5a: replace LeaveTry edges with EndFinally edges
+ // this is used only for copyFinallyBlocks==false; see Step 5b otherwise
+ void TransformLeaveEdges()
+ {
+ for (int i = nodes.Count - 1; i >= 0; i--) {
+ ControlFlowNode node = nodes[i];
+ if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) {
+ Debug.Assert(node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S);
+
+ ControlFlowNode target = node.Outgoing[0].Target;
+ // remove the edge
+ target.Incoming.Remove(node.Outgoing[0]);
+ node.Outgoing.Clear();
+
+ ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset);
+ Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
+
+ CreateEdge(node, handler, JumpType.Normal);
+ CreateEdge(handler.EndFinallyOrFaultNode, target, JumpType.EndFinally);
+ }
+ }
+ }
+ #endregion
+
+ #region Step 5b: copy finally blocks into the LeaveTry edges
+ void CopyFinallyBlocksIntoLeaveEdges()
+ {
+ // We need to process try-finally blocks inside-out.
+ // We'll do that by going through all instructions in reverse order
+ for (int i = nodes.Count - 1; i >= 0; i--) {
+ ControlFlowNode node = nodes[i];
+ if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) {
+ Debug.Assert(node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S);
+
+ ControlFlowNode target = node.Outgoing[0].Target;
+ // remove the edge
+ target.Incoming.Remove(node.Outgoing[0]);
+ node.Outgoing.Clear();
+
+ ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset);
+ Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
+
+ ControlFlowNode copy = CopyFinallySubGraph(handler, handler.EndFinallyOrFaultNode, target);
+ CreateEdge(node, copy, JumpType.Normal);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Creates a copy of all nodes pointing to 'end' and replaces those references with references to 'newEnd'.
+ /// Nodes pointing to the copied node are copied recursively to update those references, too.
+ /// This recursion stops at 'start'. The modified version of start is returned.
+ /// </summary>
+ ControlFlowNode CopyFinallySubGraph(ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd)
+ {
+ return new CopyFinallySubGraphLogic(this, start, end, newEnd).CopyFinallySubGraph();
+ }
+
+ class CopyFinallySubGraphLogic
+ {
+ readonly ControlFlowGraphBuilder builder;
+ readonly Dictionary<ControlFlowNode, ControlFlowNode> oldToNew = new Dictionary<ControlFlowNode, ControlFlowNode>();
+ readonly ControlFlowNode start;
+ readonly ControlFlowNode end;
+ readonly ControlFlowNode newEnd;
+
+ public CopyFinallySubGraphLogic(ControlFlowGraphBuilder builder, ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd)
+ {
+ this.builder = builder;
+ this.start = start;
+ this.end = end;
+ this.newEnd = newEnd;
+ }
+
+ internal ControlFlowNode CopyFinallySubGraph()
+ {
+ foreach (ControlFlowNode n in end.Predecessors) {
+ CollectNodes(n);
+ }
+ foreach (var pair in oldToNew)
+ ReconstructEdges(pair.Key, pair.Value);
+ return GetNew(start);
+ }
+
+ void CollectNodes(ControlFlowNode node)
+ {
+ if (node == end || node == newEnd)
+ throw new InvalidOperationException("unexpected cycle involving finally construct");
+ if (!oldToNew.ContainsKey(node)) {
+ int newBlockIndex = builder.nodes.Count;
+ ControlFlowNode copy;
+ switch (node.NodeType) {
+ case ControlFlowNodeType.Normal:
+ copy = new ControlFlowNode(newBlockIndex, node.Start, node.End);
+ break;
+ case ControlFlowNodeType.FinallyOrFaultHandler:
+ copy = new ControlFlowNode(newBlockIndex, node.ExceptionHandler, node.EndFinallyOrFaultNode);
+ break;
+ default:
+ // other nodes shouldn't occur when copying finally blocks
+ throw new NotSupportedException(node.NodeType.ToString());
+ }
+ copy.CopyFrom = node;
+ builder.nodes.Add(copy);
+ oldToNew.Add(node, copy);
+
+ if (node != start) {
+ foreach (ControlFlowNode n in node.Predecessors) {
+ CollectNodes(n);
+ }
+ }
+ }
+ }
+
+ void ReconstructEdges(ControlFlowNode oldNode, ControlFlowNode newNode)
+ {
+ foreach (ControlFlowEdge oldEdge in oldNode.Outgoing) {
+ builder.CreateEdge(newNode, GetNew(oldEdge.Target), oldEdge.Type);
+ }
+ }
+
+ ControlFlowNode GetNew(ControlFlowNode oldNode)
+ {
+ if (oldNode == end)
+ return newEnd;
+ ControlFlowNode newNode;
+ if (oldToNew.TryGetValue(oldNode, out newNode))
+ return newNode;
+ return oldNode;
+ }
+ }
+ #endregion
+
+ #region CreateEdge methods
+ void CreateEdge(ControlFlowNode fromNode, Instruction toInstruction, JumpType type)
+ {
+ CreateEdge(fromNode, nodes.Single(n => n.Start == toInstruction), type);
+ }
+
+ void CreateEdge(ControlFlowNode fromNode, ControlFlowNode toNode, JumpType type)
+ {
+ ControlFlowEdge edge = new ControlFlowEdge(fromNode, toNode, type);
+ fromNode.Outgoing.Add(edge);
+ toNode.Incoming.Add(edge);
+ }
+ #endregion
+
+ #region OpCode info
+ static bool CanThrowException(OpCode opcode)
+ {
+ if (opcode.OpCodeType == OpCodeType.Prefix)
+ return false;
+ return OpCodeInfo.Get(opcode).CanThrow;
+ }
+
+ static bool IsBranch(OpCode opcode)
+ {
+ if (opcode.OpCodeType == OpCodeType.Prefix)
+ return false;
+ switch (opcode.FlowControl) {
+ case FlowControl.Cond_Branch:
+ case FlowControl.Branch:
+ case FlowControl.Throw:
+ case FlowControl.Return:
+ return true;
+ case FlowControl.Next:
+ case FlowControl.Call:
+ return false;
+ default:
+ throw new NotSupportedException(opcode.FlowControl.ToString());
+ }
+ }
+ #endregion
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs
new file mode 100644
index 00000000..83294fd9
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs
@@ -0,0 +1,305 @@
+// 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.IO;
+using System.Linq;
+
+using ICSharpCode.Decompiler.Disassembler;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ /// <summary>
+ /// Type of the control flow node
+ /// </summary>
+ public enum ControlFlowNodeType
+ {
+ /// <summary>
+ /// A normal node represents a basic block.
+ /// </summary>
+ Normal,
+ /// <summary>
+ /// The entry point of the method.
+ /// </summary>
+ EntryPoint,
+ /// <summary>
+ /// The exit point of the method (every ret instruction branches to this node)
+ /// </summary>
+ RegularExit,
+ /// <summary>
+ /// This node represents leaving a method irregularly by throwing an exception.
+ /// </summary>
+ ExceptionalExit,
+ /// <summary>
+ /// This node is used as a header for exception handler blocks.
+ /// </summary>
+ CatchHandler,
+ /// <summary>
+ /// This node is used as a header for finally blocks and fault blocks.
+ /// Every leave instruction in the try block leads to the handler of the containing finally block;
+ /// and exceptional control flow also leads to this handler.
+ /// </summary>
+ FinallyOrFaultHandler,
+ /// <summary>
+ /// This node is used as footer for finally blocks and fault blocks.
+ /// Depending on the "copyFinallyBlocks" option used when creating the graph, it is connected with all leave targets using
+ /// EndFinally edges (when not copying); or with a specific leave target using a normal edge (when copying).
+ /// For fault blocks, an exception edge is used to represent the "re-throwing" of the exception.
+ /// </summary>
+ EndFinallyOrFault
+ }
+
+ /// <summary>
+ /// Represents a block in the control flow graph.
+ /// </summary>
+ public sealed class ControlFlowNode
+ {
+ /// <summary>
+ /// Index of this node in the ControlFlowGraph.Nodes collection.
+ /// </summary>
+ public readonly int BlockIndex;
+
+ /// <summary>
+ /// Gets the IL offset of this node.
+ /// </summary>
+ public readonly int Offset;
+
+ /// <summary>
+ /// Type of the node.
+ /// </summary>
+ public readonly ControlFlowNodeType NodeType;
+
+ /// <summary>
+ /// If this node is a FinallyOrFaultHandler node, this field points to the corresponding EndFinallyOrFault node.
+ /// Otherwise, this field is null.
+ /// </summary>
+ public readonly ControlFlowNode EndFinallyOrFaultNode;
+
+ /// <summary>
+ /// Visited flag, used in various algorithms.
+ /// Before using it in your algorithm, reset it to false by calling ControlFlowGraph.ResetVisited();
+ /// </summary>
+ public bool Visited;
+
+ /// <summary>
+ /// Gets whether this node is reachable. Requires that dominance is computed!
+ /// </summary>
+ public bool IsReachable {
+ get { return ImmediateDominator != null || NodeType == ControlFlowNodeType.EntryPoint; }
+ }
+
+ /// <summary>
+ /// Signalizes that this node is a copy of another node.
+ /// </summary>
+ public ControlFlowNode CopyFrom { get; internal set; }
+
+ /// <summary>
+ /// Gets the immediate dominator (the parent in the dominator tree).
+ /// Null if dominance has not been calculated; or if the node is unreachable.
+ /// </summary>
+ public ControlFlowNode ImmediateDominator { get; internal set; }
+
+ /// <summary>
+ /// List of children in the dominator tree.
+ /// </summary>
+ public readonly List<ControlFlowNode> DominatorTreeChildren = new List<ControlFlowNode>();
+
+ /// <summary>
+ /// The dominance frontier of this node.
+ /// This is the set of nodes for which this node dominates a predecessor, but which are not strictly dominated by this node.
+ /// </summary>
+ /// <remarks>
+ /// b.DominanceFrontier = { y in CFG; (exists p in predecessors(y): b dominates p) and not (b strictly dominates y)}
+ /// </remarks>
+ public HashSet<ControlFlowNode> DominanceFrontier;
+
+ /// <summary>
+ /// Start of code block represented by this node. Only set for nodetype == Normal.
+ /// </summary>
+ public readonly Instruction Start;
+
+ /// <summary>
+ /// End of the code block represented by this node. Only set for nodetype == Normal.
+ /// The end is exclusive, the end instruction itself does not belong to this block.
+ /// </summary>
+ public readonly Instruction End;
+
+ /// <summary>
+ /// Gets the exception handler associated with this node.
+ /// Only set for nodetype == CatchHandler or nodetype == FinallyOrFaultHandler.
+ /// </summary>
+ public readonly ExceptionHandler ExceptionHandler;
+
+ /// <summary>
+ /// List of incoming control flow edges.
+ /// </summary>
+ public readonly List<ControlFlowEdge> Incoming = new List<ControlFlowEdge>();
+
+ /// <summary>
+ /// List of outgoing control flow edges.
+ /// </summary>
+ public readonly List<ControlFlowEdge> Outgoing = new List<ControlFlowEdge>();
+
+ /// <summary>
+ /// Any user data
+ /// </summary>
+ public object UserData;
+
+ internal ControlFlowNode(int blockIndex, int offset, ControlFlowNodeType nodeType)
+ {
+ BlockIndex = blockIndex;
+ Offset = offset;
+ NodeType = nodeType;
+ }
+
+ internal ControlFlowNode(int blockIndex, Instruction start, Instruction end)
+ {
+ if (start == null)
+ throw new ArgumentNullException("start");
+ if (end == null)
+ throw new ArgumentNullException("end");
+ BlockIndex = blockIndex;
+ NodeType = ControlFlowNodeType.Normal;
+ Start = start;
+ End = end;
+ Offset = start.Offset;
+ }
+
+ internal ControlFlowNode(int blockIndex, ExceptionHandler exceptionHandler, ControlFlowNode endFinallyOrFaultNode)
+ {
+ BlockIndex = blockIndex;
+ NodeType = endFinallyOrFaultNode != null ? ControlFlowNodeType.FinallyOrFaultHandler : ControlFlowNodeType.CatchHandler;
+ ExceptionHandler = exceptionHandler;
+ EndFinallyOrFaultNode = endFinallyOrFaultNode;
+ Debug.Assert((exceptionHandler.HandlerType == ExceptionHandlerType.Finally || exceptionHandler.HandlerType == ExceptionHandlerType.Fault) == (endFinallyOrFaultNode != null));
+ Offset = exceptionHandler.HandlerStart.Offset;
+ }
+
+ /// <summary>
+ /// Gets all predecessors (=sources of incoming edges)
+ /// </summary>
+ public IEnumerable<ControlFlowNode> Predecessors {
+ get {
+ return Incoming.Select(e => e.Source);
+ }
+ }
+
+ /// <summary>
+ /// Gets all successors (=targets of outgoing edges)
+ /// </summary>
+ public IEnumerable<ControlFlowNode> Successors {
+ get {
+ return Outgoing.Select(e => e.Target);
+ }
+ }
+
+ /// <summary>
+ /// Gets all instructions in this node.
+ /// Returns an empty list for special nodes that don't have any instructions.
+ /// </summary>
+ public IEnumerable<Instruction> Instructions {
+ get {
+ Instruction inst = Start;
+ if (inst != null) {
+ yield return inst;
+ while (inst != End) {
+ inst = inst.Next;
+ yield return inst;
+ }
+ }
+ }
+ }
+
+ public void TraversePreOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction)
+ {
+ if (Visited)
+ return;
+ Visited = true;
+ visitAction(this);
+ foreach (ControlFlowNode t in children(this))
+ t.TraversePreOrder(children, visitAction);
+ }
+
+ public void TraversePostOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction)
+ {
+ if (Visited)
+ return;
+ Visited = true;
+ foreach (ControlFlowNode t in children(this))
+ t.TraversePostOrder(children, visitAction);
+ visitAction(this);
+ }
+
+ public override string ToString()
+ {
+ StringWriter writer = new StringWriter();
+ switch (NodeType) {
+ case ControlFlowNodeType.Normal:
+ writer.Write("Block #{0}", BlockIndex);
+ if (Start != null)
+ writer.Write(": IL_{0:x4}", Start.Offset);
+ if (End != null)
+ writer.Write(" to IL_{0:x4}", End.GetEndOffset());
+ break;
+ case ControlFlowNodeType.CatchHandler:
+ case ControlFlowNodeType.FinallyOrFaultHandler:
+ writer.Write("Block #{0}: {1}: ", BlockIndex, NodeType);
+ DisassemblerHelpers.WriteTo(ExceptionHandler, new PlainTextOutput(writer));
+ break;
+ default:
+ writer.Write("Block #{0}: {1}", BlockIndex, NodeType);
+ break;
+ }
+// if (ImmediateDominator != null) {
+// writer.WriteLine();
+// writer.Write("ImmediateDominator: #{0}", ImmediateDominator.BlockIndex);
+// }
+ if (DominanceFrontier != null && DominanceFrontier.Any()) {
+ writer.WriteLine();
+ writer.Write("DominanceFrontier: " + string.Join(",", DominanceFrontier.OrderBy(d => d.BlockIndex).Select(d => d.BlockIndex.ToString())));
+ }
+ foreach (Instruction inst in Instructions) {
+ writer.WriteLine();
+ DisassemblerHelpers.WriteTo(inst, new PlainTextOutput(writer));
+ }
+ if (UserData != null) {
+ writer.WriteLine();
+ writer.Write(UserData.ToString());
+ }
+ return writer.ToString();
+ }
+
+ /// <summary>
+ /// Gets whether <c>this</c> dominates <paramref name="node"/>.
+ /// </summary>
+ public bool Dominates(ControlFlowNode node)
+ {
+ // TODO: this can be made O(1) by numbering the dominator tree
+ ControlFlowNode tmp = node;
+ while (tmp != null) {
+ if (tmp == this)
+ return true;
+ tmp = tmp.ImmediateDominator;
+ }
+ return false;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs
new file mode 100644
index 00000000..b7b77e07
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs
@@ -0,0 +1,241 @@
+// 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 Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ /// <summary>
+ /// Detects the structure of the control flow (exception blocks and loops).
+ /// </summary>
+ public class ControlStructureDetector
+ {
+ public static ControlStructure DetectStructure(ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers, CancellationToken cancellationToken)
+ {
+ ControlStructure root = new ControlStructure(new HashSet<ControlFlowNode>(g.Nodes), g.EntryPoint, ControlStructureType.Root);
+ // First build a structure tree out of the exception table
+ DetectExceptionHandling(root, g, exceptionHandlers);
+ // Then run the loop detection.
+ DetectLoops(g, root, cancellationToken);
+ return root;
+ }
+
+ #region Exception Handling
+ static void DetectExceptionHandling(ControlStructure current, ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers)
+ {
+ // We rely on the fact that the exception handlers are sorted so that the innermost come first.
+ // For each exception handler, we determine the nodes and substructures inside that handler, and move them into a new substructure.
+ // This is always possible because exception handlers are guaranteed (by the CLR spec) to be properly nested and non-overlapping;
+ // so they directly form the tree that we need.
+ foreach (ExceptionHandler eh in exceptionHandlers) {
+ var tryNodes = FindNodes(current, eh.TryStart, eh.TryEnd);
+ current.Nodes.ExceptWith(tryNodes);
+ ControlStructure tryBlock = new ControlStructure(
+ tryNodes,
+ g.Nodes.Single(n => n.Start == eh.TryStart),
+ ControlStructureType.Try);
+ tryBlock.ExceptionHandler = eh;
+ MoveControlStructures(current, tryBlock, eh.TryStart, eh.TryEnd);
+ current.Children.Add(tryBlock);
+
+ if (eh.FilterStart != null) {
+ throw new NotSupportedException();
+ }
+
+ var handlerNodes = FindNodes(current, eh.HandlerStart, eh.HandlerEnd);
+ var handlerNode = current.Nodes.Single(n => n.ExceptionHandler == eh);
+ handlerNodes.Add(handlerNode);
+ if (handlerNode.EndFinallyOrFaultNode != null)
+ handlerNodes.Add(handlerNode.EndFinallyOrFaultNode);
+ current.Nodes.ExceptWith(handlerNodes);
+ ControlStructure handlerBlock = new ControlStructure(
+ handlerNodes, handlerNode, ControlStructureType.Handler);
+ handlerBlock.ExceptionHandler = eh;
+ MoveControlStructures(current, handlerBlock, eh.HandlerStart, eh.HandlerEnd);
+ current.Children.Add(handlerBlock);
+ }
+ }
+
+ /// <summary>
+ /// Removes all nodes from start to end (exclusive) from this ControlStructure and moves them to the target structure.
+ /// </summary>
+ static HashSet<ControlFlowNode> FindNodes(ControlStructure current, Instruction startInst, Instruction endInst)
+ {
+ HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>();
+ int start = startInst.Offset;
+ int end = endInst.Offset;
+ foreach (var node in current.Nodes.ToArray()) {
+ if (node.Start != null && start <= node.Start.Offset && node.Start.Offset < end) {
+ result.Add(node);
+ }
+ }
+ return result;
+ }
+
+ static void MoveControlStructures(ControlStructure current, ControlStructure target, Instruction startInst, Instruction endInst)
+ {
+ for (int i = 0; i < current.Children.Count; i++) {
+ var child = current.Children[i];
+ if (startInst.Offset <= child.EntryPoint.Offset && child.EntryPoint.Offset < endInst.Offset) {
+ current.Children.RemoveAt(i--);
+ target.Children.Add(child);
+ target.AllNodes.UnionWith(child.AllNodes);
+ }
+ }
+ }
+ #endregion
+
+ #region Loop Detection
+ // Loop detection works like this:
+ // We find a top-level loop by looking for its entry point, which is characterized by a node dominating its own predecessor.
+ // Then we determine all other nodes that belong to such a loop (all nodes which lead to the entry point, and are dominated by it).
+ // Finally, we check whether our result conforms with potential existing exception structures, and create the substructure for the loop if successful.
+
+ // This algorithm is applied recursively for any substructures (both detected loops and exception blocks)
+
+ // But maybe we should get rid of this complex stuff and instead treat every backward jump as a loop?
+ // That should still work with the IL produced by compilers, and has the advantage that the detected loop bodies are consecutive IL regions.
+
+ static void DetectLoops(ControlFlowGraph g, ControlStructure current, CancellationToken cancellationToken)
+ {
+ if (!current.EntryPoint.IsReachable)
+ return;
+ g.ResetVisited();
+ cancellationToken.ThrowIfCancellationRequested();
+ FindLoops(current, current.EntryPoint);
+ foreach (ControlStructure loop in current.Children)
+ DetectLoops(g, loop, cancellationToken);
+ }
+
+ static void FindLoops(ControlStructure current, ControlFlowNode node)
+ {
+ if (node.Visited)
+ return;
+ node.Visited = true;
+ if (current.Nodes.Contains(node)
+ && node.DominanceFrontier.Contains(node)
+ && !(node == current.EntryPoint && current.Type == ControlStructureType.Loop))
+ {
+ HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>();
+ FindLoopContents(current, loopContents, node, node);
+ List<ControlStructure> containedChildStructures = new List<ControlStructure>();
+ bool invalidNesting = false;
+ foreach (ControlStructure childStructure in current.Children) {
+ if (childStructure.AllNodes.IsSubsetOf(loopContents)) {
+ containedChildStructures.Add(childStructure);
+ } else if (childStructure.AllNodes.Intersect(loopContents).Any()) {
+ invalidNesting = true;
+ }
+ }
+ if (!invalidNesting) {
+ current.Nodes.ExceptWith(loopContents);
+ ControlStructure ctl = new ControlStructure(loopContents, node, ControlStructureType.Loop);
+ foreach (ControlStructure childStructure in containedChildStructures) {
+ ctl.Children.Add(childStructure);
+ current.Children.Remove(childStructure);
+ ctl.Nodes.ExceptWith(childStructure.AllNodes);
+ }
+ current.Children.Add(ctl);
+ }
+ }
+ foreach (var edge in node.Outgoing) {
+ FindLoops(current, edge.Target);
+ }
+ }
+
+ static void FindLoopContents(ControlStructure current, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode node)
+ {
+ if (current.AllNodes.Contains(node) && loopHead.Dominates(node) && loopContents.Add(node)) {
+ foreach (var edge in node.Incoming) {
+ FindLoopContents(current, loopContents, loopHead, edge.Source);
+ }
+ }
+ }
+ #endregion
+ }
+
+ public enum ControlStructureType
+ {
+ /// <summary>
+ /// The root block of the method
+ /// </summary>
+ Root,
+ /// <summary>
+ /// A nested control structure representing a loop.
+ /// </summary>
+ Loop,
+ /// <summary>
+ /// A nested control structure representing a try block.
+ /// </summary>
+ Try,
+ /// <summary>
+ /// A nested control structure representing a catch, finally, or fault block.
+ /// </summary>
+ Handler,
+ /// <summary>
+ /// A nested control structure representing an exception filter block.
+ /// </summary>
+ Filter
+ }
+
+ /// <summary>
+ /// Represents the structure detected by the <see cref="ControlStructureDetector"/>.
+ ///
+ /// This is a tree of ControlStructure nodes. Each node contains a set of CFG nodes, and every CFG node is contained in exactly one ControlStructure node.
+ /// </summary>
+ public class ControlStructure
+ {
+ public readonly ControlStructureType Type;
+ public readonly List<ControlStructure> Children = new List<ControlStructure>();
+
+ /// <summary>
+ /// The nodes in this control structure.
+ /// </summary>
+ public readonly HashSet<ControlFlowNode> Nodes;
+
+ /// <summary>
+ /// The nodes in this control structure and in all child control structures.
+ /// </summary>
+ public readonly HashSet<ControlFlowNode> AllNodes;
+
+ /// <summary>
+ /// The entry point of this control structure.
+ /// </summary>
+ public readonly ControlFlowNode EntryPoint;
+
+ /// <summary>
+ /// The exception handler associated with this Try,Handler or Finally structure.
+ /// </summary>
+ public ExceptionHandler ExceptionHandler;
+
+ public ControlStructure(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint, ControlStructureType type)
+ {
+ if (nodes == null)
+ throw new ArgumentNullException("nodes");
+ Nodes = nodes;
+ EntryPoint = entryPoint;
+ Type = type;
+ AllNodes = new HashSet<ControlFlowNode>(nodes);
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs b/ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs
new file mode 100644
index 00000000..e0dc724f
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs
@@ -0,0 +1,312 @@
+// 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 Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ /// <summary>
+ /// Additional info about opcodes.
+ /// </summary>
+ internal sealed class OpCodeInfo
+ {
+ public static bool IsUnconditionalBranch(OpCode opcode)
+ {
+ if (opcode.OpCodeType == OpCodeType.Prefix)
+ return false;
+ switch (opcode.FlowControl) {
+ case FlowControl.Branch:
+ case FlowControl.Throw:
+ case FlowControl.Return:
+ return true;
+ case FlowControl.Next:
+ case FlowControl.Call:
+ case FlowControl.Cond_Branch:
+ return false;
+ default:
+ throw new NotSupportedException(opcode.FlowControl.ToString());
+ }
+ }
+
+ static readonly OpCodeInfo[] knownOpCodes = {
+ #region Base Instructions
+ new OpCodeInfo(OpCodes.Add) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Add_Ovf) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Add_Ovf_Un) { CanThrow = true },
+ new OpCodeInfo(OpCodes.And) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Arglist) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Beq) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Beq_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Bge) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Bge_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Bge_Un) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Bge_Un_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Bgt) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Bgt_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Bgt_Un) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Bgt_Un_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ble) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ble_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ble_Un) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ble_Un_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Blt) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Blt_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Blt_Un) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Blt_Un_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Bne_Un) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Bne_Un_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Br) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Br_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Break) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Brfalse) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Brfalse_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Brtrue) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Brtrue_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Call) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Calli) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Ceq) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Cgt) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Cgt_Un) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ckfinite) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Clt) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Clt_Un) { CanThrow = false },
+ // conv.<to type>
+ new OpCodeInfo(OpCodes.Conv_I1) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Conv_I2) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Conv_I4) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Conv_I8) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Conv_R4) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Conv_R8) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Conv_U1) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Conv_U2) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Conv_U4) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Conv_U8) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Conv_I) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Conv_U) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Conv_R_Un) { CanThrow = false },
+ // conv.ovf.<to type>
+ new OpCodeInfo(OpCodes.Conv_Ovf_I1) { CanThrow = true},
+ new OpCodeInfo(OpCodes.Conv_Ovf_I2) { CanThrow = true},
+ new OpCodeInfo(OpCodes.Conv_Ovf_I4) { CanThrow = true},
+ new OpCodeInfo(OpCodes.Conv_Ovf_I8) { CanThrow = true},
+ new OpCodeInfo(OpCodes.Conv_Ovf_U1) { CanThrow = true},
+ new OpCodeInfo(OpCodes.Conv_Ovf_U2) { CanThrow = true},
+ new OpCodeInfo(OpCodes.Conv_Ovf_U4) { CanThrow = true},
+ new OpCodeInfo(OpCodes.Conv_Ovf_U8) { CanThrow = true},
+ new OpCodeInfo(OpCodes.Conv_Ovf_I) { CanThrow = true},
+ new OpCodeInfo(OpCodes.Conv_Ovf_U) { CanThrow = true},
+ // conv.ovf.<to type>.un
+ new OpCodeInfo(OpCodes.Conv_Ovf_I1_Un) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Conv_Ovf_I2_Un) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Conv_Ovf_I4_Un) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Conv_Ovf_I8_Un) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Conv_Ovf_U1_Un) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Conv_Ovf_U2_Un) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Conv_Ovf_U4_Un) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Conv_Ovf_U8_Un) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Conv_Ovf_I_Un) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Conv_Ovf_U_Un) { CanThrow = true },
+
+ //new OpCodeInfo(OpCodes.Cpblk) { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it
+ new OpCodeInfo(OpCodes.Div) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Div_Un) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Dup) { CanThrow = true, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Endfilter) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Endfinally) { CanThrow = false },
+ //new OpCodeInfo(OpCodes.Initblk) { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it
+ //new OpCodeInfo(OpCodes.Jmp) { CanThrow = true } - We don't support non-local control transfers.
+ new OpCodeInfo(OpCodes.Ldarg) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Ldarg_0) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Ldarg_1) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Ldarg_2) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Ldarg_3) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Ldarg_S) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Ldarga) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldarga_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_I4) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_I4_M1) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_I4_0) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_I4_1) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_I4_2) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_I4_3) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_I4_4) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_I4_5) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_I4_6) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_I4_7) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_I4_8) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_I4_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_I8) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_R4) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldc_R8) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldftn) { CanThrow = false },
+ // ldind.<type>
+ new OpCodeInfo(OpCodes.Ldind_I1) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Ldind_I2) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Ldind_I4) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Ldind_I8) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Ldind_U1) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Ldind_U2) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Ldind_U4) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Ldind_R4) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Ldind_R8) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Ldind_I) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Ldind_Ref) { CanThrow = true },
+ // the ldloc exceptions described in the spec can only occur on methods without .localsinit - but csc always sets that flag
+ new OpCodeInfo(OpCodes.Ldloc) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Ldloc_0) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Ldloc_1) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Ldloc_2) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Ldloc_3) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Ldloc_S) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Ldloca) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldloca_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldnull) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Leave) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Leave_S) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Localloc) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Mul) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Mul_Ovf) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Mul_Ovf_Un) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Neg) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Nop) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Not) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Or) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Pop) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Rem) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Rem_Un) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Ret) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Shl) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Shr) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Shr_Un) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Starg) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Starg_S) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Stind_I1) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Stind_I2) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Stind_I4) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Stind_I8) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Stind_R4) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Stind_R8) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Stind_I) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Stind_Ref) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Stloc) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Stloc_0) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Stloc_1) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Stloc_2) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Stloc_3) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Stloc_S) { CanThrow = false, IsMoveInstruction = true },
+ new OpCodeInfo(OpCodes.Sub) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Sub_Ovf) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Sub_Ovf_Un) { CanThrow = true },
+ new OpCodeInfo(OpCodes.Switch) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Xor) { CanThrow = false },
+ #endregion
+ #region Object model instructions
+ // CanThrow is true by default - most OO instructions can throw, so we don't specify CanThrow all of the time
+ new OpCodeInfo(OpCodes.Box),
+ new OpCodeInfo(OpCodes.Callvirt),
+ new OpCodeInfo(OpCodes.Castclass),
+ new OpCodeInfo(OpCodes.Cpobj),
+ new OpCodeInfo(OpCodes.Initobj) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Isinst) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldelem_Any),
+ // ldelem.<type>
+ new OpCodeInfo(OpCodes.Ldelem_I) ,
+ new OpCodeInfo(OpCodes.Ldelem_I1),
+ new OpCodeInfo(OpCodes.Ldelem_I2),
+ new OpCodeInfo(OpCodes.Ldelem_I4),
+ new OpCodeInfo(OpCodes.Ldelem_I8),
+ new OpCodeInfo(OpCodes.Ldelem_R4),
+ new OpCodeInfo(OpCodes.Ldelem_R8),
+ new OpCodeInfo(OpCodes.Ldelem_Ref),
+ new OpCodeInfo(OpCodes.Ldelem_U1),
+ new OpCodeInfo(OpCodes.Ldelem_U2),
+ new OpCodeInfo(OpCodes.Ldelem_U4),
+ new OpCodeInfo(OpCodes.Ldelema) ,
+ new OpCodeInfo(OpCodes.Ldfld) ,
+ new OpCodeInfo(OpCodes.Ldflda),
+ new OpCodeInfo(OpCodes.Ldlen) ,
+ new OpCodeInfo(OpCodes.Ldobj) ,
+ new OpCodeInfo(OpCodes.Ldsfld),
+ new OpCodeInfo(OpCodes.Ldsflda),
+ new OpCodeInfo(OpCodes.Ldstr) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldtoken) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Ldvirtftn),
+ new OpCodeInfo(OpCodes.Mkrefany),
+ new OpCodeInfo(OpCodes.Newarr),
+ new OpCodeInfo(OpCodes.Newobj),
+ new OpCodeInfo(OpCodes.Refanytype) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Refanyval),
+ new OpCodeInfo(OpCodes.Rethrow),
+ new OpCodeInfo(OpCodes.Sizeof) { CanThrow = false },
+ new OpCodeInfo(OpCodes.Stelem_Any),
+ new OpCodeInfo(OpCodes.Stelem_I1),
+ new OpCodeInfo(OpCodes.Stelem_I2),
+ new OpCodeInfo(OpCodes.Stelem_I4),
+ new OpCodeInfo(OpCodes.Stelem_I8),
+ new OpCodeInfo(OpCodes.Stelem_R4),
+ new OpCodeInfo(OpCodes.Stelem_R8),
+ new OpCodeInfo(OpCodes.Stelem_Ref),
+ new OpCodeInfo(OpCodes.Stfld),
+ new OpCodeInfo(OpCodes.Stobj),
+ new OpCodeInfo(OpCodes.Stsfld),
+ new OpCodeInfo(OpCodes.Throw),
+ new OpCodeInfo(OpCodes.Unbox),
+ new OpCodeInfo(OpCodes.Unbox_Any),
+ #endregion
+ };
+ static readonly Dictionary<Code, OpCodeInfo> knownOpCodeDict = knownOpCodes.ToDictionary(info => info.OpCode.Code);
+
+ public static OpCodeInfo Get(OpCode opCode)
+ {
+ return Get(opCode.Code);
+ }
+
+ public static OpCodeInfo Get(Code code)
+ {
+ OpCodeInfo info;
+ if (knownOpCodeDict.TryGetValue(code, out info))
+ return info;
+ else
+ throw new NotSupportedException(code.ToString());
+ }
+
+ OpCode opcode;
+
+ OpCodeInfo(OpCode opcode)
+ {
+ this.opcode = opcode;
+ CanThrow = true;
+ }
+
+ public OpCode OpCode { get { return opcode; } }
+
+ /// <summary>
+ /// 'Move' kind of instructions have one input (may be stack or local variable) and copy that value to all outputs (again stack or local variable).
+ /// </summary>
+ public bool IsMoveInstruction { get; private set; }
+
+ /// <summary>
+ /// Specifies whether this opcode is capable of throwing exceptions.
+ /// </summary>
+ public bool CanThrow { get; private set; }
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs b/ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs
new file mode 100644
index 00000000..42c30914
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs
@@ -0,0 +1,174 @@
+// 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 Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ /// <summary>
+ /// This is a transformation working on SSA form.
+ /// It removes ldloca instructions and replaces them with SpecialOpCode.PrepareByOutCall or SpecialOpCode.PrepareByRefCall.
+ /// This then allows the variable that had its address taken to also be transformed into SSA.
+ /// </summary>
+ internal sealed class SimplifyByRefCalls
+ {
+ public static bool MakeByRefCallsSimple(SsaForm ssaForm)
+ {
+ SimplifyByRefCalls instance = new SimplifyByRefCalls(ssaForm);
+ foreach (SsaBlock block in ssaForm.Blocks) {
+ for (int i = 0; i < block.Instructions.Count; i++) {
+ SsaInstruction inst = block.Instructions[i];
+ if (inst.Instruction != null) {
+ switch (inst.Instruction.OpCode.Code) {
+ case Code.Call:
+ case Code.Callvirt:
+ instance.MakeByRefCallSimple(block, ref i, (IMethodSignature)inst.Instruction.Operand);
+ break;
+ case Code.Initobj:
+ instance.MakeInitObjCallSimple(block, ref i);
+ break;
+ case Code.Ldfld:
+ instance.MakeLoadFieldCallSimple(block, ref i);
+ break;
+ }
+ }
+ }
+ }
+ instance.RemoveRedundantInstructions();
+ if (instance.couldSimplifySomething)
+ ssaForm.ComputeVariableUsage();
+ return instance.couldSimplifySomething;
+ }
+
+ readonly SsaForm ssaForm;
+
+ bool couldSimplifySomething;
+
+ // the list of ldloca instructions we will remove
+ readonly List<SsaInstruction> redundantLoadAddressInstructions = new List<SsaInstruction>();
+
+ SimplifyByRefCalls(SsaForm ssaForm)
+ {
+ this.ssaForm = ssaForm;
+ }
+
+ void MakeByRefCallSimple(SsaBlock block, ref int instructionIndexInBlock, IMethodSignature targetMethod)
+ {
+ SsaInstruction inst = block.Instructions[instructionIndexInBlock];
+ for (int i = 0; i < inst.Operands.Length; i++) {
+ SsaVariable operand = inst.Operands[i];
+ if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
+ // address is used for this method call only
+
+ Instruction loadAddressInstruction = operand.Definition.Instruction;
+
+ // find target parameter type:
+ bool isOut;
+ if (i == 0 && targetMethod.HasThis) {
+ isOut = false;
+ } else {
+ ParameterDefinition parameter = targetMethod.Parameters[i - (targetMethod.HasThis ? 1 : 0)];
+ isOut = parameter.IsOut;
+ }
+
+ SsaVariable addressTakenOf = GetVariableFromLoadAddressInstruction(loadAddressInstruction);
+
+ // insert "Prepare" instruction on front
+ SpecialOpCode loadOpCode = isOut ? SpecialOpCode.PrepareByOutCall : SpecialOpCode.PrepareByRefCall;
+ block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction(
+ block, null, operand, new SsaVariable[] { addressTakenOf }, specialOpCode: loadOpCode));
+
+ // insert "WriteAfterByRefOrOutCall" instruction after call
+ block.Instructions.Insert(instructionIndexInBlock + 1, new SsaInstruction(
+ block, null, addressTakenOf, new SsaVariable[] { operand }, specialOpCode: SpecialOpCode.WriteAfterByRefOrOutCall));
+
+ couldSimplifySomething = true;
+
+ // remove the loadAddressInstruction later
+ // (later because it might be defined in the current block and we don't want instructionIndex to become invalid)
+ redundantLoadAddressInstructions.Add(operand.Definition);
+ }
+ }
+ }
+
+ SsaVariable GetVariableFromLoadAddressInstruction(Instruction loadAddressInstruction)
+ {
+ if (loadAddressInstruction.OpCode == OpCodes.Ldloca) {
+ return ssaForm.GetOriginalVariable((VariableReference)loadAddressInstruction.Operand);
+ } else {
+ Debug.Assert(loadAddressInstruction.OpCode == OpCodes.Ldarga);
+ return ssaForm.GetOriginalVariable((ParameterReference)loadAddressInstruction.Operand);
+ }
+ }
+
+ static bool IsLoadAddress(SsaInstruction inst)
+ {
+ return inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Ldloca || inst.Instruction.OpCode == OpCodes.Ldarga);
+ }
+
+ void MakeInitObjCallSimple(SsaBlock block, ref int instructionIndexInBlock)
+ {
+ SsaInstruction inst = block.Instructions[instructionIndexInBlock];
+ Debug.Assert(inst.Operands.Length == 1);
+ SsaVariable operand = inst.Operands[0];
+ if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
+ // replace instruction with special "InitObj" instruction
+ block.Instructions[instructionIndexInBlock] = new SsaInstruction(
+ inst.ParentBlock, null, GetVariableFromLoadAddressInstruction(operand.Definition.Instruction), null,
+ specialOpCode: SpecialOpCode.InitObj,
+ typeOperand: (TypeReference)inst.Instruction.Operand);
+
+ couldSimplifySomething = true;
+
+ // remove the loadAddressInstruction later
+ redundantLoadAddressInstructions.Add(operand.Definition);
+ }
+ }
+
+ void MakeLoadFieldCallSimple(SsaBlock block, ref int instructionIndexInBlock)
+ {
+ SsaInstruction inst = block.Instructions[instructionIndexInBlock];
+ Debug.Assert(inst.Operands.Length == 1);
+ SsaVariable operand = inst.Operands[0];
+ if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
+ // insert special "PrepareForFieldAccess" instruction in front
+ block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction(
+ inst.ParentBlock, null, operand,
+ new SsaVariable[] { GetVariableFromLoadAddressInstruction(operand.Definition.Instruction) },
+ specialOpCode: SpecialOpCode.PrepareForFieldAccess));
+
+ couldSimplifySomething = true;
+
+ // remove the loadAddressInstruction later
+ redundantLoadAddressInstructions.Add(operand.Definition);
+ }
+ }
+
+ void RemoveRedundantInstructions()
+ {
+ foreach (SsaInstruction inst in redundantLoadAddressInstructions) {
+ inst.ParentBlock.Instructions.Remove(inst);
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs b/ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs
new file mode 100644
index 00000000..b1767b9b
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs
@@ -0,0 +1,60 @@
+// 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.IO;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ /// <summary>
+ /// A block in a control flow graph; with instructions represented by "SsaInstructions" (instructions use variables, no evaluation stack).
+ /// Usually these variables are in SSA form to make analysis easier.
+ /// </summary>
+ public sealed class SsaBlock
+ {
+ public readonly List<SsaBlock> Successors = new List<SsaBlock>();
+ public readonly List<SsaBlock> Predecessors = new List<SsaBlock>();
+ public readonly ControlFlowNodeType NodeType;
+ public readonly List<SsaInstruction> Instructions = new List<SsaInstruction>();
+
+ /// <summary>
+ /// The block index in the control flow graph.
+ /// This correspons to the node index in ControlFlowGraph.Nodes, so it can be used to retrieve the original CFG node and look
+ /// up additional information (e.g. dominance).
+ /// </summary>
+ public readonly int BlockIndex;
+
+ internal SsaBlock(ControlFlowNode node)
+ {
+ NodeType = node.NodeType;
+ BlockIndex = node.BlockIndex;
+ }
+
+ public override string ToString()
+ {
+ StringWriter writer = new StringWriter();
+ writer.Write("Block #{0} ({1})", BlockIndex, NodeType);
+ foreach (SsaInstruction inst in Instructions) {
+ writer.WriteLine();
+ inst.WriteTo(writer);
+ }
+ return writer.ToString();
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs b/ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs
new file mode 100644
index 00000000..baf520eb
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs
@@ -0,0 +1,162 @@
+// Copyright (c) 2010 Daniel Grunwald
+//
+// 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.Diagnostics;
+using System.Linq;
+
+using ICSharpCode.NRefactory.Utils;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ /// <summary>
+ /// Represents a graph of SsaBlocks.
+ /// </summary>
+ public sealed class SsaForm
+ {
+ readonly SsaVariable[] parameters;
+ readonly SsaVariable[] locals;
+ public readonly ReadOnlyCollection<SsaVariable> OriginalVariables;
+ public readonly ReadOnlyCollection<SsaBlock> Blocks;
+ readonly bool methodHasThis;
+
+ public SsaBlock EntryPoint {
+ get { return Blocks[0]; }
+ }
+
+ public SsaBlock RegularExit {
+ get { return Blocks[1]; }
+ }
+
+ public SsaBlock ExceptionalExit {
+ get { return Blocks[2]; }
+ }
+
+ internal SsaForm(SsaBlock[] blocks, SsaVariable[] parameters, SsaVariable[] locals, SsaVariable[] stackLocations, bool methodHasThis)
+ {
+ this.parameters = parameters;
+ this.locals = locals;
+ Blocks = new ReadOnlyCollection<SsaBlock>(blocks);
+ OriginalVariables = new ReadOnlyCollection<SsaVariable>(parameters.Concat(locals).Concat(stackLocations).ToList());
+ this.methodHasThis = methodHasThis;
+
+ Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint);
+ Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit);
+ Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit);
+ for (int i = 0; i < OriginalVariables.Count; i++) {
+ OriginalVariables[i].OriginalVariableIndex = i;
+ }
+ }
+
+ public GraphVizGraph ExportBlockGraph(Func<SsaBlock, string> labelProvider = null)
+ {
+ if (labelProvider == null)
+ labelProvider = b => b.ToString();
+ GraphVizGraph graph = new GraphVizGraph();
+ foreach (SsaBlock block in Blocks) {
+ graph.AddNode(new GraphVizNode(block.BlockIndex) { label = labelProvider(block), shape = "box" });
+ }
+ foreach (SsaBlock block in Blocks) {
+ foreach (SsaBlock s in block.Successors) {
+ graph.AddEdge(new GraphVizEdge(block.BlockIndex, s.BlockIndex));
+ }
+ }
+ return graph;
+ }
+
+ public GraphVizGraph ExportVariableGraph(Func<SsaVariable, string> labelProvider = null)
+ {
+ if (labelProvider == null)
+ labelProvider = v => v.ToString();
+ GraphVizGraph graph = new GraphVizGraph();
+ foreach (SsaVariable v in AllVariables) {
+ graph.AddNode(new GraphVizNode(v.Name) { label = labelProvider(v) });
+ }
+ int instructionIndex = 0;
+ foreach (SsaBlock block in Blocks) {
+ foreach (SsaInstruction inst in block.Instructions) {
+ if (inst.Operands.Length == 0 && inst.Target == null)
+ continue;
+ string id = "instruction" + (++instructionIndex);
+ graph.AddNode(new GraphVizNode(id) { label = inst.ToString(), shape = "box" });
+ foreach (SsaVariable op in inst.Operands)
+ graph.AddEdge(new GraphVizEdge(op.Name, id));
+ if (inst.Target != null)
+ graph.AddEdge(new GraphVizEdge(id, inst.Target.Name));
+ }
+ }
+ return graph;
+ }
+
+ public SsaVariable GetOriginalVariable(ParameterReference parameter)
+ {
+ if (methodHasThis)
+ return parameters[parameter.Index + 1];
+ else
+ return parameters[parameter.Index];
+ }
+
+ public SsaVariable GetOriginalVariable(VariableReference variable)
+ {
+ return locals[variable.Index];
+ }
+
+ #region ComputeVariableUsage
+ public void ComputeVariableUsage()
+ {
+ // clear data from previous runs
+ foreach (SsaBlock block in Blocks) {
+ foreach (SsaInstruction inst in block.Instructions) {
+ foreach (SsaVariable v in inst.Operands) {
+ if (v.Usage != null)
+ v.Usage.Clear();
+ }
+ if (inst.Target != null && inst.Target.Usage != null)
+ inst.Target.Usage.Clear();
+ }
+ }
+ foreach (SsaBlock block in Blocks) {
+ foreach (SsaInstruction inst in block.Instructions) {
+ foreach (SsaVariable v in inst.Operands) {
+ if (v.Usage == null)
+ v.Usage = new List<SsaInstruction>();
+ v.Usage.Add(inst);
+ }
+ if (inst.Target != null && inst.Target.Usage == null)
+ inst.Target.Usage = new List<SsaInstruction>();
+ }
+ }
+ }
+ #endregion
+
+ public IEnumerable<SsaVariable> AllVariables {
+ get {
+ return (
+ from block in Blocks
+ from instruction in block.Instructions
+ where instruction.Target != null
+ select instruction.Target
+ ).Distinct();
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs b/ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs
new file mode 100644
index 00000000..c7c86a57
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs
@@ -0,0 +1,257 @@
+// Copyright (c) 2010 Daniel Grunwald
+//
+// 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 Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ /// <summary>
+ /// Constructs "SsaForm" graph for a CFG.
+ /// This class transforms the method from stack-based IL to a register-based IL language.
+ /// Then it calls into TransformToSsa to convert the resulting graph to static single assignment form.
+ /// </summary>
+ public sealed class SsaFormBuilder
+ {
+ public static SsaForm Build(MethodDefinition method)
+ {
+ if (method == null)
+ throw new ArgumentNullException("method");
+ var cfg = ControlFlowGraphBuilder.Build(method.Body);
+ cfg.ComputeDominance();
+ cfg.ComputeDominanceFrontier();
+ var ssa = BuildRegisterIL(method, cfg);
+ TransformToSsa.Transform(cfg, ssa);
+ return ssa;
+ }
+
+ public static SsaForm BuildRegisterIL(MethodDefinition method, ControlFlowGraph cfg)
+ {
+ if (method == null)
+ throw new ArgumentNullException("method");
+ if (cfg == null)
+ throw new ArgumentNullException("cfg");
+ return new SsaFormBuilder(method, cfg).Build();
+ }
+
+ readonly MethodDefinition method;
+ readonly ControlFlowGraph cfg;
+
+ readonly SsaBlock[] blocks; // array index = block index
+ readonly int[] stackSizeAtBlockStart; // array index = block index
+
+ readonly SsaVariable[] parameters; // array index = parameter number
+ readonly SsaVariable[] locals; // array index = local number
+ readonly SsaVariable[] stackLocations; // array index = position on the IL evaluation stack
+ SsaForm ssaForm;
+
+ SsaFormBuilder(MethodDefinition method, ControlFlowGraph cfg)
+ {
+ this.method = method;
+ this.cfg = cfg;
+
+ blocks = new SsaBlock[cfg.Nodes.Count];
+ stackSizeAtBlockStart = new int[cfg.Nodes.Count];
+ for (int i = 0; i < stackSizeAtBlockStart.Length; i++) {
+ stackSizeAtBlockStart[i] = -1;
+ }
+ stackSizeAtBlockStart[cfg.EntryPoint.BlockIndex] = 0;
+
+ parameters = new SsaVariable[method.Parameters.Count + (method.HasThis ? 1 : 0)];
+ if (method.HasThis)
+ parameters[0] = new SsaVariable(method.Body.ThisParameter);
+ for (int i = 0; i < method.Parameters.Count; i++)
+ parameters[i + (method.HasThis ? 1 : 0)] = new SsaVariable(method.Parameters[i]);
+
+ locals = new SsaVariable[method.Body.Variables.Count];
+ for (int i = 0; i < locals.Length; i++)
+ locals[i] = new SsaVariable(method.Body.Variables[i]);
+
+ stackLocations = new SsaVariable[method.Body.MaxStackSize];
+ for (int i = 0; i < stackLocations.Length; i++) {
+ stackLocations[i] = new SsaVariable(i);
+ }
+ }
+
+ internal SsaForm Build()
+ {
+ CreateGraphStructure();
+ ssaForm = new SsaForm(blocks, parameters, locals, stackLocations, method.HasThis);
+ CreateInstructions(cfg.EntryPoint.BlockIndex);
+ CreateSpecialInstructions();
+ return ssaForm;
+ }
+
+ void CreateGraphStructure()
+ {
+ for (int i = 0; i < blocks.Length; i++) {
+ blocks[i] = new SsaBlock(cfg.Nodes[i]);
+ }
+ for (int i = 0; i < blocks.Length; i++) {
+ foreach (ControlFlowNode node in cfg.Nodes[i].Successors) {
+ blocks[i].Successors.Add(blocks[node.BlockIndex]);
+ blocks[node.BlockIndex].Predecessors.Add(blocks[i]);
+ }
+ }
+ }
+
+ void CreateInstructions(int blockIndex)
+ {
+ ControlFlowNode cfgNode = cfg.Nodes[blockIndex];
+ SsaBlock block = blocks[blockIndex];
+
+ int stackSize = stackSizeAtBlockStart[blockIndex];
+ Debug.Assert(stackSize >= 0);
+
+ List<Instruction> prefixes = new List<Instruction>();
+ foreach (Instruction inst in cfgNode.Instructions) {
+ if (inst.OpCode.OpCodeType == OpCodeType.Prefix) {
+ prefixes.Add(inst);
+ continue;
+ }
+
+ int popCount = inst.GetPopDelta(method) ?? stackSize;
+ stackSize -= popCount;
+ if (stackSize < 0)
+ throw new InvalidProgramException("IL stack underflow");
+
+ int pushCount = inst.GetPushDelta();
+ if (stackSize + pushCount > stackLocations.Length)
+ throw new InvalidProgramException("IL stack overflow");
+
+ SsaVariable target;
+ SsaVariable[] operands;
+ DetermineOperands(stackSize, inst, popCount, pushCount, out target, out operands);
+
+ Instruction[] prefixArray = prefixes.Count > 0 ? prefixes.ToArray() : null;
+ prefixes.Clear();
+
+ // ignore NOP instructions
+ if (!(inst.OpCode == OpCodes.Nop || inst.OpCode == OpCodes.Pop)) {
+ block.Instructions.Add(new SsaInstruction(block, inst, target, operands, prefixArray));
+ }
+ stackSize += pushCount;
+ }
+
+ foreach (ControlFlowEdge edge in cfgNode.Outgoing) {
+ int newStackSize;
+ switch (edge.Type) {
+ case JumpType.Normal:
+ newStackSize = stackSize;
+ break;
+ case JumpType.EndFinally:
+ if (stackSize != 0)
+ throw new NotSupportedException("stacksize must be 0 in endfinally edge");
+ newStackSize = 0;
+ break;
+ case JumpType.JumpToExceptionHandler:
+ switch (edge.Target.NodeType) {
+ case ControlFlowNodeType.FinallyOrFaultHandler:
+ newStackSize = 0;
+ break;
+ case ControlFlowNodeType.ExceptionalExit:
+ case ControlFlowNodeType.CatchHandler:
+ newStackSize = 1;
+ break;
+ default:
+ throw new NotSupportedException("unsupported target node type: " + edge.Target.NodeType);
+ }
+ break;
+ default:
+ throw new NotSupportedException("unsupported jump type: " + edge.Type);
+ }
+
+ int nextStackSize = stackSizeAtBlockStart[edge.Target.BlockIndex];
+ if (nextStackSize == -1) {
+ stackSizeAtBlockStart[edge.Target.BlockIndex] = newStackSize;
+ CreateInstructions(edge.Target.BlockIndex);
+ } else if (nextStackSize != newStackSize) {
+ throw new InvalidProgramException("Stack size doesn't match");
+ }
+ }
+ }
+
+ void DetermineOperands(int stackSize, Instruction inst, int popCount, int pushCount, out SsaVariable target, out SsaVariable[] operands)
+ {
+ switch (inst.OpCode.Code) {
+ case Code.Ldarg:
+ operands = new SsaVariable[] { ssaForm.GetOriginalVariable((ParameterReference)inst.Operand) };
+ target = stackLocations[stackSize];
+ break;
+ case Code.Starg:
+ operands = new SsaVariable[] { stackLocations[stackSize] };
+ target = ssaForm.GetOriginalVariable((ParameterReference)inst.Operand);
+ break;
+ case Code.Ldloc:
+ operands = new SsaVariable[] { ssaForm.GetOriginalVariable((VariableReference)inst.Operand) };
+ target = stackLocations[stackSize];
+ break;
+ case Code.Stloc:
+ operands = new SsaVariable[] { stackLocations[stackSize] };
+ target = ssaForm.GetOriginalVariable((VariableReference)inst.Operand);
+ break;
+ case Code.Dup:
+ operands = new SsaVariable[] { stackLocations[stackSize] };
+ target = stackLocations[stackSize + 1];
+ break;
+ default:
+ operands = new SsaVariable[popCount];
+ for (int i = 0; i < popCount; i++) {
+ operands[i] = stackLocations[stackSize + i];
+ }
+
+ switch (pushCount) {
+ case 0:
+ target = null;
+ break;
+ case 1:
+ target = stackLocations[stackSize];
+ break;
+ default:
+ throw new NotSupportedException("unsupported pushCount=" + pushCount);
+ }
+ break;
+ }
+ }
+
+ void CreateSpecialInstructions()
+ {
+ // Everything needs an initial write for the SSA transformation to work correctly.
+ foreach (SsaVariable v in parameters) {
+ ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Parameter));
+ }
+ foreach (SsaVariable v in locals) {
+ ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Uninitialized));
+ }
+ foreach (SsaVariable v in stackLocations) {
+ ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Uninitialized));
+ }
+ foreach (SsaBlock b in blocks) {
+ if (b.NodeType == ControlFlowNodeType.CatchHandler) {
+ b.Instructions.Add(new SsaInstruction(b, null, stackLocations[0], null,
+ specialOpCode: SpecialOpCode.Exception,
+ typeOperand: cfg.Nodes[b.BlockIndex].ExceptionHandler.CatchType));
+ }
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/SsaInstruction.cs b/ICSharpCode.Decompiler/FlowAnalysis/SsaInstruction.cs
new file mode 100644
index 00000000..c9375852
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/SsaInstruction.cs
@@ -0,0 +1,191 @@
+// 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.IO;
+
+using ICSharpCode.Decompiler.Disassembler;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ public enum SpecialOpCode
+ {
+ /// <summary>
+ /// No special op code: SsaInstruction has a normal IL instruction
+ /// </summary>
+ None,
+ /// <summary>
+ /// Φ function: chooses the appropriate variable based on which CFG edge was used to enter this block
+ /// </summary>
+ Phi,
+ /// <summary>
+ /// Variable is read from before passing it by ref.
+ /// This instruction constructs a managed reference to the variable.
+ /// </summary>
+ PrepareByRefCall,
+ /// <summary>
+ /// This instruction constructs a managed reference to the variable.
+ /// The variable is not really read from.
+ /// </summary>
+ PrepareByOutCall,
+ /// <summary>
+ /// This instruction constructs a managed reference to the variable.
+ /// The reference is used for a field access on a value type.
+ /// </summary>
+ PrepareForFieldAccess,
+ /// <summary>
+ /// Variable is written to after passing it by ref or out.
+ /// </summary>
+ WriteAfterByRefOrOutCall,
+ /// <summary>
+ /// Variable is not initialized.
+ /// </summary>
+ Uninitialized,
+ /// <summary>
+ /// Value is passed in as parameter
+ /// </summary>
+ Parameter,
+ /// <summary>
+ /// Value is a caught exception.
+ /// TypeOperand is set to the exception type.
+ /// </summary>
+ Exception,
+ /// <summary>
+ /// Initialize a value type. Unlike the real initobj instruction, this one does not take an address
+ /// but assigns to the target variable.
+ /// TypeOperand is set to the type being created.
+ /// </summary>
+ InitObj
+ }
+
+ public sealed class SsaInstruction
+ {
+ public readonly SsaBlock ParentBlock;
+ public readonly SpecialOpCode SpecialOpCode;
+
+ /// <summary>
+ /// The original IL instruction.
+ /// May be null for "invented" instructions (SpecialOpCode != None).
+ /// </summary>
+ public readonly Instruction Instruction;
+
+ /// <summary>
+ /// Prefixes in front of the IL instruction.
+ /// </summary>
+ public readonly Instruction[] Prefixes;
+
+ /// <summary>
+ /// Gets the type operand. This is used only in combination with some special opcodes.
+ /// </summary>
+ public readonly TypeReference TypeOperand;
+
+ public SsaVariable Target;
+ public SsaVariable[] Operands;
+
+ static readonly SsaVariable[] emptyVariableArray = {};
+ static readonly Instruction[] emptyInstructionArray = {};
+
+ public SsaInstruction(SsaBlock parentBlock, Instruction instruction, SsaVariable target, SsaVariable[] operands,
+ Instruction[] prefixes = null, SpecialOpCode specialOpCode = SpecialOpCode.None,
+ TypeReference typeOperand = null)
+ {
+ ParentBlock = parentBlock;
+ Instruction = instruction;
+ Prefixes = prefixes ?? emptyInstructionArray;
+ Target = target;
+ Operands = operands ?? emptyVariableArray;
+ SpecialOpCode = specialOpCode;
+ TypeOperand = typeOperand;
+ Debug.Assert((typeOperand != null) == (specialOpCode == SpecialOpCode.Exception || specialOpCode == SpecialOpCode.InitObj));
+ }
+
+ /// <summary>
+ /// Gets whether this instruction is a simple assignment from one variable to another.
+ /// </summary>
+ public bool IsMoveInstruction {
+ get {
+ return Target != null && Operands.Length == 1 && Instruction != null && OpCodeInfo.Get(Instruction.OpCode).IsMoveInstruction;
+ }
+ }
+
+ public void ReplaceVariableInOperands(SsaVariable oldVar, SsaVariable newVar)
+ {
+ for (int i = 0; i < Operands.Length; i++) {
+ if (Operands[i] == oldVar)
+ Operands[i] = newVar;
+ }
+ }
+
+ public override string ToString()
+ {
+ StringWriter w = new StringWriter();
+ WriteTo(w);
+ return w.ToString();
+ }
+
+ public void WriteTo(TextWriter writer)
+ {
+ foreach (Instruction prefix in Prefixes) {
+ DisassemblerHelpers.WriteTo(prefix, new PlainTextOutput(writer));
+ writer.WriteLine();
+ }
+ if (Instruction != null && Instruction.Offset >= 0) {
+ writer.Write(CecilExtensions.OffsetToString(Instruction.Offset));
+ writer.Write(": ");
+ }
+ if (Target != null) {
+ writer.Write(Target.ToString());
+ writer.Write(" = ");
+ }
+ if (IsMoveInstruction) {
+ writer.Write(Operands[0].ToString());
+ if (Instruction != null) {
+ writer.Write(" (" + Instruction.OpCode.Name + ")");
+ }
+ } else {
+ if (Instruction == null) {
+ writer.Write(SpecialOpCode.ToString());
+ } else {
+ writer.Write(Instruction.OpCode.Name);
+ if(null != Instruction.Operand) {
+ writer.Write(' ');
+ DisassemblerHelpers.WriteOperand(new PlainTextOutput(writer), Instruction.Operand);
+ writer.Write(' ');
+ }
+ }
+ if (TypeOperand != null) {
+ writer.Write(' ');
+ writer.Write(TypeOperand.ToString());
+ writer.Write(' ');
+ }
+ if (Operands.Length > 0) {
+ writer.Write('(');
+ for (int i = 0; i < Operands.Length; i++) {
+ if (i > 0)
+ writer.Write(", ");
+ writer.Write(Operands[i].ToString());
+ }
+ writer.Write(')');
+ }
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs b/ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs
new file mode 100644
index 00000000..71696992
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs
@@ -0,0 +1,138 @@
+// 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 Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ /// <summary>
+ /// Contains some very simple optimizations that work on the SSA form.
+ /// </summary>
+ internal static class SsaOptimization
+ {
+ public static void Optimize(SsaForm ssaForm)
+ {
+ DirectlyStoreToVariables(ssaForm);
+ SimpleCopyPropagation(ssaForm);
+ RemoveDeadAssignments(ssaForm);
+ }
+
+ /// <summary>
+ /// When any instructions stores its result in a stack location that's used only once in a 'stloc' or 'starg' instruction,
+ /// we optimize this to directly store in the target location.
+ /// As optimization this is redundant (does the same as copy propagation), but it'll make us keep the variables named
+ /// after locals instead of keeping the temps as using only the simple copy propagation would do.
+ /// </summary>
+ public static void DirectlyStoreToVariables(SsaForm ssaForm)
+ {
+ foreach (SsaBlock block in ssaForm.Blocks) {
+ block.Instructions.RemoveAll(
+ inst => {
+ if (inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Stloc || inst.Instruction.OpCode == OpCodes.Starg)) {
+ SsaVariable target = inst.Target;
+ SsaVariable temp = inst.Operands[0];
+ if (target.IsSingleAssignment && temp.IsSingleAssignment && temp.Usage.Count == 1 && temp.IsStackLocation) {
+ temp.Definition.Target = target;
+ return true;
+ }
+ }
+ return false;
+ });
+ }
+ ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
+ }
+
+ public static void SimpleCopyPropagation(SsaForm ssaForm, bool onlyForStackLocations = true)
+ {
+ foreach (SsaBlock block in ssaForm.Blocks) {
+ foreach (SsaInstruction inst in block.Instructions) {
+ if (inst.IsMoveInstruction && inst.Target.IsSingleAssignment && inst.Operands[0].IsSingleAssignment) {
+ if (inst.Target.IsStackLocation || !onlyForStackLocations) {
+ // replace all uses of 'target' with 'operands[0]'.
+ foreach (SsaInstruction useInstruction in inst.Target.Usage) {
+ useInstruction.ReplaceVariableInOperands(inst.Target, inst.Operands[0]);
+ }
+ }
+ }
+ }
+ }
+ ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
+ }
+
+ public static void RemoveDeadAssignments(SsaForm ssaForm)
+ {
+ HashSet<SsaVariable> liveVariables = new HashSet<SsaVariable>();
+ // find variables that are used directly
+ foreach (SsaBlock block in ssaForm.Blocks) {
+ foreach (SsaInstruction inst in block.Instructions) {
+ if (!CanRemoveAsDeadCode(inst)) {
+ if (inst.Target != null)
+ liveVariables.Add(inst.Target);
+ foreach (SsaVariable op in inst.Operands) {
+ liveVariables.Add(op);
+ }
+ }
+ }
+ }
+ Queue<SsaVariable> queue = new Queue<SsaVariable>(liveVariables);
+ // find variables that are used indirectly
+ while (queue.Count > 0) {
+ SsaVariable v = queue.Dequeue();
+ if (v.IsSingleAssignment) {
+ foreach (SsaVariable op in v.Definition.Operands) {
+ if (liveVariables.Add(op))
+ queue.Enqueue(op);
+ }
+ }
+ }
+ // remove assignments to all unused variables
+ foreach (SsaBlock block in ssaForm.Blocks) {
+ block.Instructions.RemoveAll(
+ inst => {
+ if (inst.Target != null && !liveVariables.Contains(inst.Target)) {
+ Debug.Assert(inst.Target.IsSingleAssignment);
+ return true;
+ }
+ return false;
+ });
+ }
+ ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
+ }
+
+ static bool CanRemoveAsDeadCode(SsaInstruction inst)
+ {
+ if (inst.Target != null && !inst.Target.IsSingleAssignment)
+ return false;
+ switch (inst.SpecialOpCode) {
+ case SpecialOpCode.Phi:
+ case SpecialOpCode.Exception:
+ case SpecialOpCode.Parameter:
+ case SpecialOpCode.Uninitialized:
+ return true;
+ case SpecialOpCode.None:
+ return inst.IsMoveInstruction;
+ default:
+ return false;
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs b/ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs
new file mode 100644
index 00000000..902b7002
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs
@@ -0,0 +1,91 @@
+// 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 Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ /// <summary>
+ /// Represents a variable used with the SsaInstruction register-based instructions.
+ /// Despite what the name suggests, the variable is not necessarily in single-assignment form - take a look at "bool IsSingleAssignment".
+ /// </summary>
+ public sealed class SsaVariable
+ {
+ public int OriginalVariableIndex;
+ public readonly string Name;
+ public readonly bool IsStackLocation;
+
+ public readonly ParameterDefinition Parameter;
+ public readonly VariableDefinition Variable;
+
+ public SsaVariable(ParameterDefinition p)
+ {
+ Name = string.IsNullOrEmpty(p.Name) ? "param" + p.Index : p.Name;
+ Parameter = p;
+ }
+
+ public SsaVariable(VariableDefinition v)
+ {
+ Name = string.IsNullOrEmpty(v.Name) ? "V_" + v.Index : v.Name;
+ Variable = v;
+ }
+
+ public SsaVariable(int stackLocation)
+ {
+ Name = "stack" + stackLocation;
+ IsStackLocation = true;
+ }
+
+ public SsaVariable(SsaVariable original, string newName)
+ {
+ Name = newName;
+ IsStackLocation = original.IsStackLocation;
+ OriginalVariableIndex = original.OriginalVariableIndex;
+ Parameter = original.Parameter;
+ Variable = original.Variable;
+ }
+
+ public override string ToString()
+ {
+ return Name;
+ }
+
+ /// <summary>
+ /// Gets whether this variable has only a single assignment.
+ /// This field is initialized in TransformToSsa step.
+ /// </summary>
+ /// <remarks>Not all variables can be transformed to single assignment form: variables that have their address taken
+ /// cannot be represented in SSA (although SimplifyByRefCalls will get rid of the address-taking instruction in almost all cases)</remarks>
+ public bool IsSingleAssignment;
+
+ /// <summary>
+ /// Gets the instruction defining the variable.
+ /// This field is initialized in TransformToSsa step. It is only set for variables with a single assignment.
+ /// </summary>
+ public SsaInstruction Definition;
+
+ /// <summary>
+ /// Gets the places where a variable is used.
+ /// If a single instruction reads a variable 2 times (e.g. adding to itself), then it must be included 2 times in this list!
+ /// </summary>
+ public List<SsaInstruction> Usage;
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs b/ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs
new file mode 100644
index 00000000..47ff7bff
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs
@@ -0,0 +1,254 @@
+// 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.Diagnostics;
+using System.Linq;
+
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ /// <summary>
+ /// Convers a method to static single assignment form.
+ /// </summary>
+ internal sealed class TransformToSsa
+ {
+ public static void Transform(ControlFlowGraph cfg, SsaForm ssa, bool optimize = true)
+ {
+ TransformToSsa transform = new TransformToSsa(cfg, ssa);
+ transform.ConvertVariablesToSsa();
+ SsaOptimization.RemoveDeadAssignments(ssa); // required so that 'MakeByRefCallsSimple' can detect more cases
+ if (SimplifyByRefCalls.MakeByRefCallsSimple(ssa)) {
+ transform.ConvertVariablesToSsa();
+ }
+ if (optimize)
+ SsaOptimization.Optimize(ssa);
+ }
+
+ readonly ControlFlowGraph cfg;
+ readonly SsaForm ssaForm;
+ readonly List<SsaInstruction>[] writeToOriginalVariables; // array index -> SsaVariable OriginalVariableIndex
+ readonly bool[] addressTaken; // array index -> SsaVariable OriginalVariableIndex; value = whether ldloca instruction was used with variable
+
+ TransformToSsa(ControlFlowGraph cfg, SsaForm ssaForm)
+ {
+ this.cfg = cfg;
+ this.ssaForm = ssaForm;
+ writeToOriginalVariables = new List<SsaInstruction>[ssaForm.OriginalVariables.Count];
+ addressTaken = new bool[ssaForm.OriginalVariables.Count];
+ }
+
+ #region CollectInformationAboutOriginalVariableUse
+ void CollectInformationAboutOriginalVariableUse()
+ {
+ Debug.Assert(addressTaken.Length == writeToOriginalVariables.Length);
+ for (int i = 0; i < writeToOriginalVariables.Length; i++) {
+ Debug.Assert(ssaForm.OriginalVariables[i].OriginalVariableIndex == i);
+
+ addressTaken[i] = false;
+ // writeToOriginalVariables is only used when placing phi functions
+ // we don't need to do that anymore for variables that are already in SSA form
+ if (ssaForm.OriginalVariables[i].IsSingleAssignment)
+ writeToOriginalVariables[i] = null;
+ else
+ writeToOriginalVariables[i] = new List<SsaInstruction>();
+ }
+ foreach (SsaBlock block in ssaForm.Blocks) {
+ foreach (SsaInstruction inst in block.Instructions) {
+ if (inst.Target != null ) {
+ var list = writeToOriginalVariables[inst.Target.OriginalVariableIndex];
+ if (list != null)
+ list.Add(inst);
+ }
+ if (inst.Instruction != null) {
+ if (inst.Instruction.OpCode == OpCodes.Ldloca) {
+ addressTaken[ssaForm.GetOriginalVariable((VariableDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true;
+ } else if (inst.Instruction.OpCode == OpCodes.Ldarga) {
+ addressTaken[ssaForm.GetOriginalVariable((ParameterDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true;
+ }
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region ConvertToSsa
+ void ConvertVariablesToSsa()
+ {
+ CollectInformationAboutOriginalVariableUse();
+ bool[] processVariable = new bool[ssaForm.OriginalVariables.Count];
+ foreach (SsaVariable variable in ssaForm.OriginalVariables) {
+ if (!variable.IsSingleAssignment && !addressTaken[variable.OriginalVariableIndex]) {
+ PlacePhiFunctions(variable);
+ processVariable[variable.OriginalVariableIndex] = true;
+ }
+ }
+ RenameVariables(processVariable);
+ foreach (SsaVariable variable in ssaForm.OriginalVariables) {
+ if (!addressTaken[variable.OriginalVariableIndex]) {
+ Debug.Assert(variable.IsSingleAssignment && variable.Definition != null);
+ }
+ }
+ ssaForm.ComputeVariableUsage();
+ }
+ #endregion
+
+ #region PlacePhiFunctions
+ void PlacePhiFunctions(SsaVariable variable)
+ {
+ cfg.ResetVisited();
+ HashSet<SsaBlock> blocksWithPhi = new HashSet<SsaBlock>();
+ Queue<ControlFlowNode> worklist = new Queue<ControlFlowNode>();
+ foreach (SsaInstruction writeInstruction in writeToOriginalVariables[variable.OriginalVariableIndex]) {
+ ControlFlowNode cfgNode = cfg.Nodes[writeInstruction.ParentBlock.BlockIndex];
+ if (!cfgNode.Visited) {
+ cfgNode.Visited = true;
+ worklist.Enqueue(cfgNode);
+ }
+ }
+ while (worklist.Count > 0) {
+ ControlFlowNode cfgNode = worklist.Dequeue();
+ foreach (ControlFlowNode dfNode in cfgNode.DominanceFrontier) {
+ // we don't need phi functions in the exit node
+ if (dfNode.NodeType == ControlFlowNodeType.RegularExit || dfNode.NodeType == ControlFlowNodeType.ExceptionalExit)
+ continue;
+ SsaBlock y = ssaForm.Blocks[dfNode.BlockIndex];
+ if (blocksWithPhi.Add(y)) {
+ // add a phi instruction in y
+ SsaVariable[] operands = Enumerable.Repeat(variable, dfNode.Incoming.Count).ToArray();
+ y.Instructions.Insert(0, new SsaInstruction(y, null, variable, operands, specialOpCode: SpecialOpCode.Phi));
+ if (!dfNode.Visited) {
+ dfNode.Visited = true;
+ worklist.Enqueue(dfNode);
+ }
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region RenameVariable
+ int tempVariableCounter = 1;
+
+ void RenameVariables(bool[] processVariable)
+ {
+ VariableRenamer r = new VariableRenamer(this, processVariable);
+ r.Visit(ssaForm.EntryPoint);
+ }
+
+ sealed class VariableRenamer
+ {
+ readonly TransformToSsa transform;
+ readonly ReadOnlyCollection<SsaVariable> inputVariables;
+ internal readonly Stack<SsaVariable>[] versionStacks;
+ int[] versionCounters; // specifies for each input variable the next version number
+
+ // processVariable = specifies for each input variable whether we should rename it
+ public VariableRenamer(TransformToSsa transform, bool[] processVariable)
+ {
+ this.transform = transform;
+ inputVariables = transform.ssaForm.OriginalVariables;
+ Debug.Assert(inputVariables.Count == processVariable.Length);
+ versionCounters = new int[inputVariables.Count];
+ versionStacks = new Stack<SsaVariable>[inputVariables.Count];
+ for (int i = 0; i < versionStacks.Length; i++) {
+ if (processVariable[i]) {
+ Debug.Assert(inputVariables[i].IsSingleAssignment == false);
+ // only create version stacks for the variables that we need to process and that weren't already processed earlier
+ versionStacks[i] = new Stack<SsaVariable>();
+ versionStacks[i].Push(inputVariables[i]);
+ }
+ }
+ }
+
+ SsaVariable MakeNewVersion(int variableIndex)
+ {
+ int versionCounter = ++versionCounters[variableIndex];
+ SsaVariable x = inputVariables[variableIndex];
+ if (versionCounter == 1) {
+ return x;
+ } else {
+ if (x.IsStackLocation) {
+ return new SsaVariable(x, "temp" + (transform.tempVariableCounter++));
+ } else {
+ return new SsaVariable(x, x.Name + "_" + versionCounter);
+ }
+ }
+ }
+
+ internal void Visit(SsaBlock block)
+ {
+ // duplicate top of all stacks
+ foreach (var stack in versionStacks) {
+ if (stack != null)
+ stack.Push(stack.Peek());
+ }
+
+ foreach (SsaInstruction s in block.Instructions) {
+ // replace all uses of variables being processed with their current version.
+ if (s.SpecialOpCode != SpecialOpCode.Phi) {
+ for (int i = 0; i < s.Operands.Length; i++) {
+ var stack = versionStacks[s.Operands[i].OriginalVariableIndex];
+ if (stack != null)
+ s.Operands[i] = stack.Peek();
+ }
+ }
+ // if we're writing to a variable we should process:
+ if (s.Target != null) {
+ int targetIndex = s.Target.OriginalVariableIndex;
+ if (versionStacks[targetIndex] != null) {
+ s.Target = MakeNewVersion(targetIndex);
+ s.Target.IsSingleAssignment = true;
+ s.Target.Definition = s;
+
+ // we already pushed our entry for this SsaBlock at the beginning (where we duplicated all stacks),
+ // so now replace the top element
+ versionStacks[targetIndex].Pop();
+ versionStacks[targetIndex].Push(s.Target);
+ }
+ }
+ }
+
+ foreach (SsaBlock succ in block.Successors) {
+ int j = succ.Predecessors.IndexOf(block);
+ Debug.Assert(j >= 0);
+ foreach (SsaInstruction f in succ.Instructions) {
+ if (f.SpecialOpCode == SpecialOpCode.Phi) {
+ var stack = versionStacks[f.Target.OriginalVariableIndex];
+ if (stack != null) {
+ f.Operands[j] = stack.Peek();
+ }
+ }
+ }
+ }
+ foreach (ControlFlowNode child in transform.cfg.Nodes[block.BlockIndex].DominatorTreeChildren)
+ Visit(transform.ssaForm.Blocks[child.BlockIndex]);
+ // restore stacks:
+ foreach (var stack in versionStacks) {
+ if (stack != null)
+ stack.Pop();
+ }
+ }
+ }
+ #endregion
+ }
+}