diff options
author | Mike Krüger <mkrueger@xamarin.com> | 2015-08-14 08:19:35 +0300 |
---|---|---|
committer | Mike Krüger <mkrueger@xamarin.com> | 2015-08-14 08:19:35 +0300 |
commit | 6e89be0c1970091167e888c1f3565e62c215fd75 (patch) | |
tree | a9be66b48b4a510b41006b2d30ba4160f79b5903 /main/contrib | |
parent | 6e0f0a9547094cfa6d9b5fc6a151691b667c4d3c (diff) |
Fixed 'Bug 33024 - Decompiler fails on most classes'
Diffstat (limited to 'main/contrib')
36 files changed, 815 insertions, 284 deletions
diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Annotations.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Annotations.cs index 7b22dd0136..1c957292ec 100644 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Annotations.cs +++ b/main/contrib/ICSharpCode.Decompiler/Ast/Annotations.cs @@ -10,10 +10,12 @@ namespace ICSharpCode.Decompiler.Ast public class TypeInformation { public readonly TypeReference InferredType; + public readonly TypeReference ExpectedType; - public TypeInformation(TypeReference inferredType) + public TypeInformation(TypeReference inferredType, TypeReference expectedType) { this.InferredType = inferredType; + this.ExpectedType = expectedType; } } diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/main/contrib/ICSharpCode.Decompiler/Ast/AstBuilder.cs index b488cf4840..82d6a71554 100644 --- a/main/contrib/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/main/contrib/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -176,14 +176,19 @@ namespace ICSharpCode.Decompiler.Ast RunTransformations(); syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); - var outputFormatter = new TextTokenWriter(output) { FoldBraces = context.Settings.FoldBraces }; + 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) { - if (assemblyDefinition.Name.Version != null) { + 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", @@ -192,22 +197,24 @@ namespace ICSharpCode.Decompiler.Ast Type = new SimpleType("AssemblyVersion") .WithAnnotation(new TypeReference( "System.Reflection", "AssemblyVersionAttribute", - assemblyDefinition.MainModule, assemblyDefinition.MainModule.TypeSystem.Corlib)), + moduleDefinition, moduleDefinition.TypeSystem.Corlib)), Arguments = { - new PrimitiveExpression(assemblyDefinition.Name.Version.ToString()) + new PrimitiveExpression(moduleDefinition.Assembly.Name.Version.ToString()) } } } }, EntityDeclaration.AttributeRole); } - ConvertCustomAttributes(syntaxTree, assemblyDefinition, "assembly"); - ConvertSecurityAttributes(syntaxTree, assemblyDefinition, "assembly"); - ConvertCustomAttributes(syntaxTree, assemblyDefinition.MainModule, "module"); - AddTypeForwarderAttributes(syntaxTree, assemblyDefinition.MainModule, "assembly"); + 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 assemblyDefinition.MainModule.Types) { + foreach (TypeDefinition typeDef in moduleDefinition.Types) { // Skip the <Module> class if (typeDef.Name == "<Module>") continue; // Skip any hidden types @@ -330,19 +337,22 @@ namespace ICSharpCode.Decompiler.Ast if (typeDef.IsEnum) { long expectedEnumMemberValue = 0; bool forcePrintingInitializers = IsFlagsEnum(typeDef); + TypeCode baseType = TypeCode.Int32; foreach (FieldDefinition field in typeDef.Fields) { if (!field.IsStatic) { // the value__ field if (field.FieldType != typeDef.Module.TypeSystem.Int32) { astType.AddChild(ConvertType(field.FieldType), Roles.BaseType); + baseType = TypeAnalysis.GetTypeCode(field.FieldType); } } else { EnumMemberDeclaration enumMember = new EnumMemberDeclaration(); + ConvertCustomAttributes(enumMember, field); enumMember.AddAnnotation(field); enumMember.Name = CleanName(field.Name); long memberValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false); if (forcePrintingInitializers || memberValue != expectedEnumMemberValue) { - enumMember.AddChild(new PrimitiveExpression(field.Constant), EnumMemberDeclaration.InitializerRole); + enumMember.AddChild(new PrimitiveExpression(CSharpPrimitiveCast.Cast(baseType, field.Constant, false)), EnumMemberDeclaration.InitializerRole); } expectedEnumMemberValue = memberValue + 1; astType.AddChild(enumMember, Roles.TypeMemberRole); diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/main/contrib/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index fae41f6860..e98e8dbc71 100644 --- a/main/contrib/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/main/contrib/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -52,8 +52,8 @@ namespace ICSharpCode.Decompiler.Ast /// 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) + DecompilerContext context, + IEnumerable<ParameterDeclaration> parameters = null) { MethodDefinition oldCurrentMethod = context.CurrentMethod; Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef); @@ -103,8 +103,8 @@ namespace ICSharpCode.Decompiler.Ast 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 })) + join v in astBuilder.Parameters on p.Annotation<ParameterDefinition>() equals v.OriginalParameter + select new { p, v.Name })) { pair.p.Name = pair.Name; } @@ -204,7 +204,7 @@ namespace ICSharpCode.Decompiler.Ast tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock); foreach (var catchClause in tryCatchNode.CatchBlocks) { if (catchClause.ExceptionVariable == null - && (catchClause.ExceptionType == null || catchClause.ExceptionType.MetadataType == MetadataType.Object)) + && (catchClause.ExceptionType == null || catchClause.ExceptionType.MetadataType == MetadataType.Object)) { tryCatchStmt.CatchClauses.Add(new Ast.CatchClause { Body = TransformBlock(catchClause) }); } else { @@ -262,7 +262,7 @@ namespace ICSharpCode.Decompiler.Ast result = node; if (result != null) - result = result.WithAnnotation(new TypeInformation(expr.InferredType)); + result = result.WithAnnotation(new TypeInformation(expr.InferredType, expr.ExpectedType)); if (result != null) return result.WithAnnotation(ilRanges); @@ -291,16 +291,10 @@ namespace ICSharpCode.Decompiler.Ast { BinaryOperatorExpression boe; if (byteCode.InferredType is PointerType) { - if (byteCode.Arguments[0].ExpectedType is PointerType) { - arg2 = DivideBySize(arg2, ((PointerType)byteCode.InferredType).ElementType); - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); - boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); - } else if (byteCode.Arguments[1].ExpectedType is PointerType) { - arg1 = DivideBySize(arg1, ((PointerType)byteCode.InferredType).ElementType); - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); + boe = new Ast.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 Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); } } else { boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); @@ -314,12 +308,9 @@ namespace ICSharpCode.Decompiler.Ast { BinaryOperatorExpression boe; if (byteCode.InferredType is PointerType) { + boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); if (byteCode.Arguments[0].ExpectedType is PointerType) { - arg2 = DivideBySize(arg2, ((PointerType)byteCode.InferredType).ElementType); - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); boe.WithAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation); - } else { - boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); } } else { boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2); @@ -460,12 +451,30 @@ namespace ICSharpCode.Decompiler.Ast // 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 Ast.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 Ast.PrimitiveExpression; + if (p != null && p.Value.IsZero()) goto case ILCode.Ceq; + } + goto case ILCode.Cle; } case ILCode.Cle: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); @@ -706,7 +715,7 @@ namespace ICSharpCode.Decompiler.Ast } return new StackAllocExpression { Type = AstBuilder.ConvertType(type), - CountExpression = DivideBySize(arg1, type) + CountExpression = arg1 }; } case ILCode.Mkrefany: @@ -849,7 +858,7 @@ namespace ICSharpCode.Decompiler.Ast args[args.Count - 1].AddAnnotation(new ParameterDeclarationAnnotation(byteCode)); return args[args.Count - 1]; case ILCode.Await: - return new UnaryOperatorExpression(UnaryOperatorType.Await, arg1); + return new UnaryOperatorExpression(UnaryOperatorType.Await, UnpackDirectionExpression(arg1)); case ILCode.NullableOf: case ILCode.ValueOf: return arg1; @@ -902,45 +911,6 @@ namespace ICSharpCode.Decompiler.Ast } } - /// <summary> - /// Divides expr by the size of 'type'. - /// </summary> - Expression DivideBySize(Expression expr, TypeReference type) - { - CastExpression cast = expr as CastExpression; - if (cast != null && cast.Type is PrimitiveType && ((PrimitiveType)cast.Type).Keyword == "int") - expr = cast.Expression.Detach(); - - Expression sizeOfExpression; - switch (TypeAnalysis.GetInformationAmount(type)) { - case 1: - case 8: - sizeOfExpression = new PrimitiveExpression(1); - break; - case 16: - sizeOfExpression = new PrimitiveExpression(2); - break; - case 32: - sizeOfExpression = new PrimitiveExpression(4); - break; - case 64: - sizeOfExpression = new PrimitiveExpression(8); - break; - default: - sizeOfExpression = new SizeOfExpression { Type = AstBuilder.ConvertType(type) }; - break; - } - - BinaryOperatorExpression boe = expr as BinaryOperatorExpression; - if (boe != null && boe.Operator == BinaryOperatorType.Multiply && sizeOfExpression.IsMatch(boe.Right)) - return boe.Left.Detach(); - - if (sizeOfExpression.IsMatch(expr)) - return new PrimitiveExpression(1); - - return new BinaryOperatorExpression(expr, BinaryOperatorType.Divide, sizeOfExpression); - } - Expression MakeDefaultValue(TypeReference type) { TypeDefinition typeDef = type.Resolve(); @@ -975,10 +945,7 @@ namespace ICSharpCode.Decompiler.Ast // Unpack any DirectionExpression that is used as target for the call // (calling methods on value types implicitly passes the first argument by reference) - if (target is DirectionExpression) { - target = ((DirectionExpression)target).Expression; - target.Remove(); // detach from DirectionExpression - } + target = UnpackDirectionExpression(target); if (cecilMethodDef != null) { // convert null.ToLower() to ((string)null).ToLower() @@ -1078,6 +1045,15 @@ namespace ICSharpCode.Decompiler.Ast 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 diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs b/main/contrib/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs index b77284cb24..4f52b89d0d 100644 --- a/main/contrib/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs +++ b/main/contrib/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs @@ -30,6 +30,7 @@ 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; @@ -40,15 +41,22 @@ namespace ICSharpCode.Decompiler.Ast public bool FoldBraces = false; - public TextTokenWriter(ITextOutput output) + 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) { + if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) { + output.Write('@'); + } + var definition = GetCurrentDefinition(); if (definition != null) { output.WriteDefinition(identifier.Name, definition, false); @@ -89,6 +97,24 @@ namespace ICSharpCode.Decompiler.Ast 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; } diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs index 3b38762651..ed1155ba15 100644 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs +++ b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs @@ -144,6 +144,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms 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) { @@ -268,6 +277,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } } } + + 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) { @@ -285,6 +303,19 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } 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) { diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 9c5aa97f6e..0741466f95 100644 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -180,8 +180,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms 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); @@ -189,11 +189,19 @@ namespace ICSharpCode.Decompiler.Ast.Transforms Expression returnExpr = ((ReturnStatement)body.Statements.Single()).Expression; returnExpr.Remove(); lambda.Body = returnExpr; - objectCreateExpression.ReplaceWith(lambda); + replacement = lambda; } else { ame.Body = body; - objectCreateExpression.ReplaceWith(ame); + 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; } diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs index 32f7cb0dbe..ffd6f5ac76 100644 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs +++ b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs @@ -804,7 +804,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms return new IndexerExpression(targetConverted, indexConverted); } IList<Expression> indexesConverted = ConvertExpressionsArray(index); - if (indexConverted != null) { + if (indexesConverted != null) { return new IndexerExpression(targetConverted, indexesConverted); } return null; diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs index 43548e38d4..a9e72564f3 100644 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs +++ b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs @@ -35,7 +35,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms protected override bool VisitChildren(AstNode node, object data) { bool result = false; - for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { + AstNode next; + for (AstNode child = node.FirstChild; child != null; child = next) { + // Store next to allow the loop to continue + // if the visitor removes/replaces child. + next = child.NextSibling; result |= child.AcceptVisitor(this, data); } if (result && node is EntityDeclaration && !(node is Accessor)) { diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs index 5c3784fa4c..4182eec8f2 100644 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs +++ b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs @@ -38,24 +38,23 @@ namespace ICSharpCode.Decompiler.Ast.Transforms public void Run(AstNode compilationUnit) { - if (!context.Settings.UsingDeclarations) - return; - // 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 - // 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] }; + 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); } - compilationUnit.InsertChildAfter(null, new UsingDeclaration { Import = nsType }, SyntaxTree.MemberRole); } if (!context.Settings.FullyQualifyAmbiguousTypeNames) diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index 22d1afe627..bff2cc6b95 100644 --- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -99,6 +99,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms if (result != null) return result; } + AstNode simplifiedIfElse = SimplifyCascadingIfElseStatements(ifElseStatement); + if (simplifiedIfElse != null) + return simplifiedIfElse; return base.VisitIfElseStatement(ifElseStatement, data); } @@ -614,6 +617,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms 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 { @@ -626,21 +630,57 @@ namespace ICSharpCode.Decompiler.Ast.Transforms new IfElseStatement { Condition = new Backreference("flag"), TrueStatement = new BlockStatement { - new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Exit", new NamedNode("exit", new IdentifierExpression(Pattern.AnyString))) + 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) { - Match m1 = lockFlagInitPattern.Match(node); - if (!m1.Success) return null; - AstNode tryCatch = node.NextSibling; - Match m2 = lockTryCatchPattern.Match(tryCatch); - if (!m2.Success) return null; - if (m1.Get<IdentifierExpression>("variable").Single().Identifier == m2.Get<IdentifierExpression>("flag").Single().Identifier) { - Expression enter = m2.Get<Expression>("enter").Single(); - IdentifierExpression exit = m2.Get<IdentifierExpression>("exit").Single(); + 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; @@ -656,7 +696,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms LockStatement l = new LockStatement(); l.Expression = enter.Detach(); l.EmbeddedStatement = ((TryCatchStatement)tryCatch).TryBlock.Detach(); - ((BlockStatement)l.EmbeddedStatement).Statements.First().Remove(); // Remove 'Enter()' call + if (!isV2) // Remove 'Enter()' call + ((BlockStatement)l.EmbeddedStatement).Statements.First().Remove(); tryCatch.ReplaceWith(l); node.Remove(); // remove flag variable return l; @@ -1047,5 +1088,36 @@ namespace ICSharpCode.Decompiler.Ast.Transforms 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/main/contrib/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs b/main/contrib/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs index 8de45cca56..f0f5ece532 100644 --- a/main/contrib/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs +++ b/main/contrib/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs @@ -32,9 +32,11 @@ namespace ICSharpCode.Decompiler.Ast if (resolveTypeArguments) return BaseTypes(derivedType).Any(t => t.Item == baseType); else { - var comparableBaseType = baseType.ResolveOrThrow(); + var comparableBaseType = baseType.Resolve(); + if (comparableBaseType == null) + return false; while (derivedType.BaseType != null) { - var resolvedBaseType = derivedType.BaseType.ResolveOrThrow(); + var resolvedBaseType = derivedType.BaseType.Resolve(); if (resolvedBaseType == null) return false; if (comparableBaseType == resolvedBaseType) @@ -185,24 +187,32 @@ namespace ICSharpCode.Decompiler.Ast if (derivedType == null) throw new ArgumentNullException("derivedType"); - var visibility = IsVisibleFromDerived(baseMember); - if (visibility.HasValue) - return visibility.Value; + MethodAttributes attrs = GetAccessAttributes(baseMember) & MethodAttributes.MemberAccessMask; + if (attrs == MethodAttributes.Private) + return false; if (baseMember.DeclaringType.Module == derivedType.Module) return true; - // TODO: Check also InternalsVisibleToAttribute. - return false; - } - private static bool? IsVisibleFromDerived(IMemberDefinition member) - { - MethodAttributes attrs = GetAccessAttributes(member) & MethodAttributes.MemberAccessMask; - if (attrs == MethodAttributes.Private) + 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; - if (attrs == MethodAttributes.Assembly || attrs == MethodAttributes.FamANDAssem) - return null; - return true; + } + + return true; } private static MethodAttributes GetAccessAttributes(IMemberDefinition member) diff --git a/main/contrib/ICSharpCode.Decompiler/CecilExtensions.cs b/main/contrib/ICSharpCode.Decompiler/CecilExtensions.cs index cc047e09cd..0c7cb1e409 100644 --- a/main/contrib/ICSharpCode.Decompiler/CecilExtensions.cs +++ b/main/contrib/ICSharpCode.Decompiler/CecilExtensions.cs @@ -126,6 +126,41 @@ namespace ICSharpCode.Decompiler 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> @@ -190,6 +225,7 @@ namespace ICSharpCode.Decompiler return null; } + [Obsolete("throwing exceptions is considered a bug")] public static TypeDefinition ResolveOrThrow(this TypeReference typeReference) { var resolved = typeReference.Resolve(); @@ -323,5 +359,16 @@ namespace ICSharpCode.Decompiler } 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/main/contrib/ICSharpCode.Decompiler/DecompilerException.cs b/main/contrib/ICSharpCode.Decompiler/DecompilerException.cs index d62b516da4..5f5f022fc5 100644 --- a/main/contrib/ICSharpCode.Decompiler/DecompilerException.cs +++ b/main/contrib/ICSharpCode.Decompiler/DecompilerException.cs @@ -23,7 +23,7 @@ using Mono.Cecil; namespace ICSharpCode.Decompiler { /// <summary> - /// Desctiption of DecompilerException. + /// Description of DecompilerException. /// </summary> public class DecompilerException : Exception, ISerializable { diff --git a/main/contrib/ICSharpCode.Decompiler/DecompilerSettings.cs b/main/contrib/ICSharpCode.Decompiler/DecompilerSettings.cs index c3790203da..a354291ed2 100644 --- a/main/contrib/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/main/contrib/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -295,6 +295,21 @@ namespace ICSharpCode.Decompiler } } + 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> @@ -318,6 +333,7 @@ namespace ICSharpCode.Decompiler if (csharpFormattingOptions == null) { csharpFormattingOptions = FormattingOptionsFactory.CreateAllman(); csharpFormattingOptions.IndentSwitchBody = false; + csharpFormattingOptions.ArrayInitializerWrapping = Wrapping.WrapAlways; } return csharpFormattingOptions; } diff --git a/main/contrib/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/main/contrib/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs index 4c2080045e..ea6e54dbea 100644 --- a/main/contrib/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs +++ b/main/contrib/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs @@ -359,6 +359,10 @@ namespace ICSharpCode.Decompiler.Disassembler } 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); @@ -375,6 +379,10 @@ namespace ICSharpCode.Decompiler.Disassembler } 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); diff --git a/main/contrib/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/main/contrib/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs index a45a718207..ef6d32e69d 100644 --- a/main/contrib/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs +++ b/main/contrib/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs @@ -54,8 +54,8 @@ namespace ICSharpCode.Decompiler.Disassembler 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.EntryPoint == method) - output.WriteLine (".entrypoint"); + if (method.DeclaringType.Module.Assembly != null && method.DeclaringType.Module.Assembly.EntryPoint == method) + output.WriteLine (".entrypoint"); if (method.Body.HasVariables) { output.Write(".locals "); diff --git a/main/contrib/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/main/contrib/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 1a7d1a4f71..fcdb2b926e 100644 --- a/main/contrib/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/main/contrib/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.Disassembler /// </summary> public sealed class ReflectionDisassembler { - ITextOutput output; + readonly ITextOutput output; CancellationToken cancellationToken; bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings) MethodBodyDisassembler methodBodyDisassembler; @@ -213,8 +213,9 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine(); } } + WriteParameterAttributes(0, method.MethodReturnType, method.MethodReturnType); foreach (var p in method.Parameters) { - WriteParameterAttributes(p); + WriteParameterAttributes(p.Index + 1, p, p); } WriteSecurityDeclarations(method); @@ -613,22 +614,17 @@ namespace ICSharpCode.Decompiler.Disassembler } } - bool HasParameterAttributes(ParameterDefinition p) + void WriteParameterAttributes(int index, IConstantProvider cp, ICustomAttributeProvider cap) { - return p.HasConstant || p.HasCustomAttributes; - } - - void WriteParameterAttributes(ParameterDefinition p) - { - if (!HasParameterAttributes(p)) + if (!cp.HasConstant && !cap.HasCustomAttributes) return; - output.Write(".param [{0}]", p.Index + 1); - if (p.HasConstant) { + output.Write(".param [{0}]", index); + if (cp.HasConstant) { output.Write(" = "); - WriteConstant(p.Constant); + WriteConstant(cp.Constant); } output.WriteLine(); - WriteAttributes(p.CustomAttributes); + WriteAttributes(cap.CustomAttributes); } void WriteConstant(object constant) diff --git a/main/contrib/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/main/contrib/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index b46b95935f..08ad03daf0 100644 --- a/main/contrib/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/main/contrib/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -56,6 +56,7 @@ <Compile Include="Ast\DecompilerContext.cs" /> <Compile Include="Ast\NameVariables.cs" /> <Compile Include="Ast\NRefactoryExtensions.cs" /> + <Compile Include="Ast\TextTokenWriter.cs" /> <Compile Include="Ast\Transforms\AddCheckedBlocks.cs" /> <Compile Include="Ast\Transforms\CombineQueryExpressions.cs" /> <Compile Include="Ast\Transforms\ContextTrackingVisitor.cs" /> @@ -97,6 +98,8 @@ <Compile Include="FlowAnalysis\SsaOptimization.cs" /> <Compile Include="FlowAnalysis\SsaVariable.cs" /> <Compile Include="FlowAnalysis\TransformToSsa.cs" /> + <Compile Include="ILAst\AsyncDecompiler.cs" /> + <Compile Include="ILAst\LiftedOperators.cs" /> <Compile Include="ILAst\InitializerPeepholeTransforms.cs" /> <Compile Include="ILAst\DefaultDictionary.cs" /> <Compile Include="ILAst\GotoRemoval.cs" /> @@ -109,6 +112,8 @@ <Compile Include="ILAst\PatternMatching.cs" /> <Compile Include="ILAst\PeepholeTransform.cs" /> <Compile Include="ILAst\SimpleControlFlow.cs" /> + <Compile Include="ILAst\StateRange.cs" /> + <Compile Include="ILAst\SymbolicExecution.cs" /> <Compile Include="ILAst\TypeAnalysis.cs" /> <Compile Include="ILAst\YieldReturnDecompiler.cs" /> <Compile Include="ITextOutput.cs" /> @@ -116,11 +121,6 @@ <Compile Include="ReferenceResolvingException.cs" /> <Compile Include="TextOutputWriter.cs" /> <None Include="Properties\AssemblyInfo.template.cs" /> - <Compile Include="ILAst\LiftedOperators.cs" /> - <Compile Include="ILAst\StateRange.cs" /> - <Compile Include="ILAst\SymbolicExecution.cs" /> - <Compile Include="ILAst\AsyncDecompiler.cs" /> - <Compile Include="Ast\TextTokenWriter.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\external\cecil\Mono.Cecil.csproj"> @@ -147,4 +147,4 @@ <Reference Include="System.Xml" /> <Reference Include="System.Xml.Linq" /> </ItemGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs index 4535c0f82a..c8a7f9b76a 100644 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs +++ b/main/contrib/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs @@ -58,6 +58,7 @@ namespace ICSharpCode.Decompiler.ILAst FieldDefinition builderField; FieldDefinition stateField; Dictionary<FieldDefinition, ILVariable> fieldToParameterMap = new Dictionary<FieldDefinition, ILVariable>(); + ILVariable cachedStateVar; // These fields are set by AnalyzeMoveNext() int finalState = -2; @@ -155,7 +156,7 @@ namespace ICSharpCode.Decompiler.ILAst ILExpression loadStateMachineForBuilderExpr; if (!loadBuilderExpr.Match(ILCode.Ldfld, out builderFieldRef, out loadStateMachineForBuilderExpr)) return false; - if (!loadStateMachineForBuilderExpr.MatchLdloca(stateMachineVar)) + if (!(loadStateMachineForBuilderExpr.MatchLdloca(stateMachineVar) || loadStateMachineForBuilderExpr.MatchLdloc(stateMachineVar))) return false; builderField = builderFieldRef.ResolveWithinSameModule(); if (builderField == null) @@ -235,36 +236,50 @@ namespace ICSharpCode.Decompiler.ILAst { ILBlock ilMethod = CreateILAst(moveNextMethod); - if (ilMethod.Body.Count != 6) + int startIndex; + if (ilMethod.Body.Count == 6) { + startIndex = 0; + } else if (ilMethod.Body.Count == 7) { + // stloc(cachedState, ldfld(valuetype StateMachineStruct::<>1__state, ldloc(this))) + ILExpression cachedStateInit; + if (!ilMethod.Body[0].Match(ILCode.Stloc, out cachedStateVar, out cachedStateInit)) + throw new SymbolicAnalysisFailedException(); + ILExpression instanceExpr; + FieldReference loadedField; + if (!cachedStateInit.Match(ILCode.Ldfld, out loadedField, out instanceExpr) || loadedField.ResolveWithinSameModule() != stateField || !instanceExpr.MatchThis()) + throw new SymbolicAnalysisFailedException(); + startIndex = 1; + } else { throw new SymbolicAnalysisFailedException(); + } - mainTryCatch = ilMethod.Body[0] as ILTryCatchBlock; + mainTryCatch = ilMethod.Body[startIndex + 0] as ILTryCatchBlock; if (mainTryCatch == null || mainTryCatch.CatchBlocks.Count != 1) throw new SymbolicAnalysisFailedException(); if (mainTryCatch.FaultBlock != null || mainTryCatch.FinallyBlock != null) throw new SymbolicAnalysisFailedException(); - setResultAndExitLabel = ilMethod.Body[1] as ILLabel; + setResultAndExitLabel = ilMethod.Body[startIndex + 1] as ILLabel; if (setResultAndExitLabel == null) throw new SymbolicAnalysisFailedException(); - if (!MatchStateAssignment(ilMethod.Body[2], out finalState)) + if (!MatchStateAssignment(ilMethod.Body[startIndex + 2], out finalState)) throw new SymbolicAnalysisFailedException(); // call(AsyncTaskMethodBuilder`1::SetResult, ldflda(StateMachine::<>t__builder, ldloc(this)), ldloc(<>t__result)) MethodReference setResultMethod; ILExpression builderExpr; if (methodType == AsyncMethodType.TaskOfT) { - if (!ilMethod.Body[3].Match(ILCode.Call, out setResultMethod, out builderExpr, out resultExpr)) + if (!ilMethod.Body[startIndex + 3].Match(ILCode.Call, out setResultMethod, out builderExpr, out resultExpr)) throw new SymbolicAnalysisFailedException(); } else { - if (!ilMethod.Body[3].Match(ILCode.Call, out setResultMethod, out builderExpr)) + if (!ilMethod.Body[startIndex + 3].Match(ILCode.Call, out setResultMethod, out builderExpr)) throw new SymbolicAnalysisFailedException(); } if (!(setResultMethod.Name == "SetResult" && IsBuilderFieldOnThis(builderExpr))) throw new SymbolicAnalysisFailedException(); - exitLabel = ilMethod.Body[4] as ILLabel; + exitLabel = ilMethod.Body[startIndex + 4] as ILLabel; if (exitLabel == null) throw new SymbolicAnalysisFailedException(); } @@ -318,7 +333,7 @@ namespace ICSharpCode.Decompiler.ILAst bool MatchStateAssignment(ILNode stfld, out int stateID) { - // stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(-2)) + // stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(stateId)) stateID = 0; FieldReference fieldRef; ILExpression target, val; @@ -329,6 +344,31 @@ namespace ICSharpCode.Decompiler.ILAst } return false; } + + bool MatchRoslynStateAssignment(List<ILNode> block, int index, out int stateID) + { + // v = ldc.i4(stateId) + // stloc(cachedState, v) + // stfld(StateMachine::<>1__state, ldloc(this), v) + stateID = 0; + if (index < 0) + return false; + ILVariable v; + ILExpression val; + if (!block[index].Match(ILCode.Stloc, out v, out val) || !val.Match(ILCode.Ldc_I4, out stateID)) + return false; + ILExpression loadV; + if (!block[index + 1].MatchStloc(cachedStateVar, out loadV) || !loadV.MatchLdloc(v)) + return false; + ILExpression target; + FieldReference fieldRef; + if (block[index + 2].Match(ILCode.Stfld, out fieldRef, out target, out loadV)) { + return fieldRef.ResolveWithinSameModule() == stateField + && target.MatchThis() + && loadV.MatchLdloc(v); + } + return false; + } #endregion #region AnalyzeStateMachine @@ -345,7 +385,7 @@ namespace ICSharpCode.Decompiler.ILAst if (body.Count == 0) throw new SymbolicAnalysisFailedException(); } - StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.AsyncMoveNext, stateField); + StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.AsyncMoveNext, stateField, cachedStateVar); int bodyLength = block.Body.Count; int pos = rangeAnalysis.AssignStateRanges(body, bodyLength); rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength); @@ -405,7 +445,7 @@ namespace ICSharpCode.Decompiler.ILAst var tryBody = tryCatchBlock.TryBlock.Body; if (tryBody.Count == 0) throw new SymbolicAnalysisFailedException(); - StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(tryBody[0], StateRangeAnalysisMode.AsyncMoveNext, stateField); + StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(tryBody[0], StateRangeAnalysisMode.AsyncMoveNext, stateField, cachedStateVar); int tryBodyLength = tryBody.Count; int posInTryBody = rangeAnalysis.AssignStateRanges(tryBody, tryBodyLength); rangeAnalysis.EnsureLabelAtPos(tryBody, ref posInTryBody, ref tryBodyLength); @@ -435,18 +475,16 @@ namespace ICSharpCode.Decompiler.ILAst List<ILNode> ConvertFinally(List<ILNode> body) { List<ILNode> newBody = new List<ILNode>(body); + if (newBody.Count == 0) + return newBody; ILLabel endFinallyLabel; ILExpression ceqExpr; - if (newBody.Count > 0 && newBody[0].Match(ILCode.Brtrue, out endFinallyLabel, out ceqExpr)) { - ILExpression loadDoFinallyBodies, loadZero; - object unused; - if (ceqExpr.Match(ILCode.Ceq, out unused, out loadDoFinallyBodies, out loadZero)) { - int num; - if (loadDoFinallyBodies.MatchLdloc(doFinallyBodies) && loadZero.Match(ILCode.Ldc_I4, out num) && num == 0) { + if (newBody[0].Match(ILCode.Brtrue, out endFinallyLabel, out ceqExpr)) { + ILExpression condition; + if (MatchLogicNot(ceqExpr, out condition)) { + if (condition.MatchLdloc(doFinallyBodies)) { newBody.RemoveAt(0); - } - } else if (ceqExpr.Match(ILCode.LogicNot, out loadDoFinallyBodies)) { - if (loadDoFinallyBodies.MatchLdloc(doFinallyBodies)) { + } else if (condition.Code == ILCode.Clt && condition.Arguments[0].MatchLdloc(cachedStateVar) && condition.Arguments[1].MatchLdcI4(0)) { newBody.RemoveAt(0); } } @@ -454,6 +492,17 @@ namespace ICSharpCode.Decompiler.ILAst return newBody; } + bool MatchLogicNot(ILExpression expr, out ILExpression arg) + { + ILExpression loadZero; + object unused; + if (expr.Match(ILCode.Ceq, out unused, out arg, out loadZero)) { + int num; + return loadZero.Match(ILCode.Ldc_I4, out num) && num == 0; + } + return expr.Match(ILCode.LogicNot, out arg); + } + void HandleAwait(List<ILNode> newBody, out ILVariable awaiterVar, out FieldDefinition awaiterField, out int targetStateID) { // Handle the instructions prior to the exit out of the method to detect what is being awaited. @@ -475,7 +524,8 @@ namespace ICSharpCode.Decompiler.ILAst newBody.RemoveAt(newBody.Count - 1); // remove AwaitUnsafeOnCompleted call if (callAwaitUnsafeOnCompleted == null || callAwaitUnsafeOnCompleted.Code != ILCode.Call) throw new SymbolicAnalysisFailedException(); - if (((MethodReference)callAwaitUnsafeOnCompleted.Operand).Name != "AwaitUnsafeOnCompleted") + string methodName = ((MethodReference)callAwaitUnsafeOnCompleted.Operand).Name; + if (methodName != "AwaitUnsafeOnCompleted" && methodName != "AwaitOnCompleted") throw new SymbolicAnalysisFailedException(); if (callAwaitUnsafeOnCompleted.Arguments.Count != 3) throw new SymbolicAnalysisFailedException(); @@ -493,9 +543,10 @@ namespace ICSharpCode.Decompiler.ILAst throw new SymbolicAnalysisFailedException(); // stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(0)) - if (!MatchStateAssignment(newBody.LastOrDefault(), out targetStateID)) - throw new SymbolicAnalysisFailedException(); - newBody.RemoveAt(newBody.Count - 1); // remove awaiter field assignment + if (MatchStateAssignment(newBody.LastOrDefault(), out targetStateID)) + newBody.RemoveAt(newBody.Count - 1); // remove awaiter field assignment + else if (MatchRoslynStateAssignment(newBody, newBody.Count - 3, out targetStateID)) + newBody.RemoveRange(newBody.Count - 3, 3); // remove awaiter field assignment } #endregion diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index da8c6c6beb..e30398e042 100644 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -413,11 +413,11 @@ namespace ICSharpCode.Decompiler.ILAst } } - // Occasionally the compilers or obfuscators generate unreachable code (which might be intentonally invalid) - // I belive it is safe to just remove it + // Occasionally the compilers or obfuscators generate unreachable code (which might be intentionally invalid) + // I believe it is safe to just remove it body.RemoveAll(b => b.StackBefore == null); - // Genertate temporary variables to replace stack + // Generate temporary variables to replace stack foreach(ByteCode byteCode in body) { int argIdx = 0; int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length; @@ -652,7 +652,7 @@ namespace ICSharpCode.Decompiler.ILAst // Find the first and widest scope int tryStart = ehs.Min(eh => eh.TryStart.Offset); int tryEnd = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset); - var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).OrderBy(eh => eh.TryStart.Offset).ToList(); + var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).ToList(); // Remember that any part of the body migt have been removed due to unreachability diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 5de51b9b1a..ce62816d7a 100644 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -20,9 +20,11 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.NRefactory.Utils; using Mono.Cecil; using Mono.Cecil.Cil; +using Mono.CSharp; namespace ICSharpCode.Decompiler.ILAst { @@ -37,6 +39,7 @@ namespace ICSharpCode.Decompiler.ILAst PropertyAccessInstructions, SplitToMovableBlocks, TypeInference, + HandlePointerArithmetic, SimplifyShortCircuit, SimplifyTernaryOperator, SimplifyNullCoalescing, @@ -123,6 +126,9 @@ namespace ICSharpCode.Decompiler.ILAst // Types are needed for the ternary operator optimization TypeAnalysis.Run(context, method); + if (abortBeforeStep == ILAstOptimizationStep.HandlePointerArithmetic) return; + HandlePointerArithmetic(method); + foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { bool modified; do { @@ -168,11 +174,15 @@ namespace ICSharpCode.Decompiler.ILAst modified |= block.RunOptimization(TransformObjectInitializers); if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return; - modified |= block.RunOptimization(MakeAssignmentExpression); + if (context.Settings.MakeAssignmentExpressions) { + modified |= block.RunOptimization(MakeAssignmentExpression); + } modified |= block.RunOptimization(MakeCompoundAssignments); if (abortBeforeStep == ILAstOptimizationStep.IntroducePostIncrement) return; - modified |= block.RunOptimization(IntroducePostIncrement); + if (context.Settings.IntroduceIncrementAndDecrement) { + modified |= block.RunOptimization(IntroducePostIncrement); + } if (abortBeforeStep == ILAstOptimizationStep.InlineExpressionTreeParameterDeclarations) return; if (context.Settings.ExpressionTrees) { @@ -223,11 +233,13 @@ namespace ICSharpCode.Decompiler.ILAst new ILInlining(method).InlineAllVariables(); if (abortBeforeStep == ILAstOptimizationStep.CachedDelegateInitialization) return; - foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { - for (int i = 0; i < block.Body.Count; i++) { - // TODO: Move before loops - CachedDelegateInitializationWithField(block, ref i); - CachedDelegateInitializationWithLocal(block, ref i); + if (context.Settings.AnonymousMethods) { + foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { + for (int i = 0; i < block.Body.Count; i++) { + // TODO: Move before loops + CachedDelegateInitializationWithField(block, ref i); + CachedDelegateInitializationWithLocal(block, ref i); + } } } @@ -337,15 +349,15 @@ namespace ICSharpCode.Decompiler.ILAst expr.ILRanges.Clear(); continue; case ILCode.__Brfalse: op = ILCode.LogicNot; break; - case ILCode.__Beq: op = ILCode.Ceq; break; + case ILCode.__Beq: op = ILCode.Ceq; break; case ILCode.__Bne_Un: op = ILCode.Cne; break; - case ILCode.__Bgt: op = ILCode.Cgt; break; + case ILCode.__Bgt: op = ILCode.Cgt; break; case ILCode.__Bgt_Un: op = ILCode.Cgt_Un; break; - case ILCode.__Ble: op = ILCode.Cle; break; + case ILCode.__Ble: op = ILCode.Cle; break; case ILCode.__Ble_Un: op = ILCode.Cle_Un; break; - case ILCode.__Blt: op = ILCode.Clt; break; + case ILCode.__Blt: op = ILCode.Clt; break; case ILCode.__Blt_Un: op = ILCode.Clt_Un; break; - case ILCode.__Bge: op = ILCode.Cge; break; + case ILCode.__Bge: op = ILCode.Cge; break; case ILCode.__Bge_Un: op = ILCode.Cge_Un; break; default: continue; @@ -424,9 +436,9 @@ namespace ICSharpCode.Decompiler.ILAst // Might be 'newobj(SomeDelegate, target, ldvirtftn(F, target))'. ILVariable target; if (expr.Arguments[0].Match(ILCode.Ldloc, out target) - && expr.Arguments[1].Code == ILCode.Ldvirtftn - && expr.Arguments[1].Arguments.Count == 1 - && expr.Arguments[1].Arguments[0].MatchLdloc(target)) + && expr.Arguments[1].Code == ILCode.Ldvirtftn + && expr.Arguments[1].Arguments.Count == 1 + && expr.Arguments[1].Arguments[0].MatchLdloc(target)) { // Remove the 'target' argument from the ldvirtftn instruction. // It's not needed in the translation to C#, and needs to be eliminated so that the target expression @@ -461,9 +473,9 @@ namespace ICSharpCode.Decompiler.ILAst // Start a new basic block if necessary if (currNode is ILLabel || - currNode is ILTryCatchBlock || // Counts as label - lastNode.IsConditionalControlFlow() || - lastNode.IsUnconditionalControlFlow()) + currNode is ILTryCatchBlock || // Counts as label + lastNode.IsConditionalControlFlow() || + lastNode.IsUnconditionalControlFlow()) { // Try to reuse the label ILLabel label = currNode as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++).ToString() }; @@ -656,6 +668,180 @@ namespace ICSharpCode.Decompiler.ILAst return combinedVariable; }); } + + void HandlePointerArithmetic(ILNode method) + { + foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) { + List<ILExpression> args = expr.Arguments; + switch (expr.Code) { + case ILCode.Localloc: + { + PointerType type = expr.InferredType as PointerType; + if (type != null) { + ILExpression arg0 = args[0]; + ILExpression expr2 = expr; + DivideOrMultiplyBySize(ref expr2, ref arg0, type.ElementType, true); + // expr shouldn't change + if (expr2 != expr) + throw new InvalidOperationException(); + args[0] = arg0; + } + break; + } + case ILCode.Add: + case ILCode.Add_Ovf: + case ILCode.Add_Ovf_Un: + { + ILExpression arg0 = args[0]; + ILExpression arg1 = args[1]; + if (expr.InferredType is PointerType) { + if (arg0.ExpectedType is PointerType) { + DivideOrMultiplyBySize(ref arg0, ref arg1, ((PointerType)expr.InferredType).ElementType, true); + } else if (arg1.ExpectedType is PointerType) + DivideOrMultiplyBySize(ref arg1, ref arg0, ((PointerType)expr.InferredType).ElementType, true); + } + args[0] = arg0; + args[1] = arg1; + break; + } + case ILCode.Sub: + case ILCode.Sub_Ovf: + case ILCode.Sub_Ovf_Un: + { + ILExpression arg0 = args[0]; + ILExpression arg1 = args[1]; + if (expr.InferredType is PointerType) { + if (arg0.ExpectedType is PointerType && !(arg1.InferredType is PointerType)) + DivideOrMultiplyBySize(ref arg0, ref arg1, ((PointerType)expr.InferredType).ElementType, true); + } + args[0] = arg0; + args[1] = arg1; + break; + } + case ILCode.Conv_I8: + { + ILExpression arg0 = args[0]; + // conv.i8(div:intptr(p0 - p1)) + if (arg0.Code == ILCode.Div && arg0.InferredType.FullName == "System.IntPtr") + { + ILExpression dividend = arg0.Arguments[0]; + if (dividend.InferredType.FullName == "System.IntPtr" && + (dividend.Code == ILCode.Sub || dividend.Code == ILCode.Sub_Ovf || dividend.Code == ILCode.Sub_Ovf_Un)) + { + PointerType pointerType0 = dividend.Arguments[0].InferredType as PointerType; + PointerType pointerType1 = dividend.Arguments[1].InferredType as PointerType; + + if (pointerType0 != null && pointerType1 != null) { + if (pointerType0.ElementType.FullName == "System.Void" || + pointerType0.ElementType.FullName != pointerType1.ElementType.FullName) { + pointerType0 = pointerType1 = new PointerType(typeSystem.Byte); + dividend.Arguments[0] = Cast(dividend.Arguments[0], pointerType0); + dividend.Arguments[1] = Cast(dividend.Arguments[1], pointerType1); + } + + DivideOrMultiplyBySize(ref dividend, ref arg0, pointerType0.ElementType, false); + // dividend shouldn't change + if (args[0].Arguments[0] != dividend) + throw new InvalidOperationException(); + } + } + } + args[0] = arg0; + break; + } + } + } + } + + static ILExpression UnwrapIntPtrCast(ILExpression expr) + { + if (expr.Code != ILCode.Conv_I && expr.Code != ILCode.Conv_U) + return expr; + + ILExpression arg = expr.Arguments[0]; + switch (arg.InferredType.MetadataType) { + case MetadataType.Byte: + case MetadataType.SByte: + case MetadataType.UInt16: + case MetadataType.Int16: + case MetadataType.UInt32: + case MetadataType.Int32: + case MetadataType.UInt64: + case MetadataType.Int64: + return arg; + } + + return expr; + } + + static ILExpression Cast(ILExpression expr, TypeReference type) + { + return new ILExpression(ILCode.Castclass, type, expr) + { + InferredType = type, + ExpectedType = type + }; + } + + void DivideOrMultiplyBySize(ref ILExpression pointerExpr, ref ILExpression adjustmentExpr, TypeReference elementType, bool divide) + { + adjustmentExpr = UnwrapIntPtrCast(adjustmentExpr); + + ILExpression sizeOfExpression; + switch (TypeAnalysis.GetInformationAmount(elementType)) { + case 0: // System.Void + pointerExpr = Cast(pointerExpr, new PointerType(typeSystem.Byte)); + goto case 1; + case 1: + case 8: + sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 1); + break; + case 16: + sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 2); + break; + case 32: + sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 4); + break; + case 64: + sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 8); + break; + default: + sizeOfExpression = new ILExpression(ILCode.Sizeof, elementType); + break; + } + + if (divide && (adjustmentExpr.Code == ILCode.Mul || adjustmentExpr.Code == ILCode.Mul_Ovf || adjustmentExpr.Code == ILCode.Mul_Ovf_Un) || + !divide && (adjustmentExpr.Code == ILCode.Div || adjustmentExpr.Code == ILCode.Div_Un)) { + ILExpression mulArg = adjustmentExpr.Arguments[1]; + if (mulArg.Code == sizeOfExpression.Code && sizeOfExpression.Operand.Equals(mulArg.Operand)) { + adjustmentExpr = UnwrapIntPtrCast(adjustmentExpr.Arguments[0]); + return; + } + } + + if (adjustmentExpr.Code == sizeOfExpression.Code) { + if (sizeOfExpression.Operand.Equals(adjustmentExpr.Operand)) { + adjustmentExpr = new ILExpression(ILCode.Ldc_I4, 1); + return; + } + + if (adjustmentExpr.Code == ILCode.Ldc_I4) { + int offsetInBytes = (int)adjustmentExpr.Operand; + int elementSize = (int)sizeOfExpression.Operand; + + if (offsetInBytes % elementSize != 0) { + pointerExpr = Cast(pointerExpr, new PointerType(typeSystem.Byte)); + return; + } + + adjustmentExpr.Operand = offsetInBytes / elementSize; + return; + } + } + + if (!(sizeOfExpression.Code == ILCode.Ldc_I4 && (int)sizeOfExpression.Operand == 1)) + adjustmentExpr = new ILExpression(divide ? ILCode.Div_Un : ILCode.Mul, null, adjustmentExpr, sizeOfExpression); + } public static void ReplaceVariables(ILNode node, Func<ILVariable, ILVariable> variableMapping) { diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 7fd74a60e8..9cd42a14fc 100644 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -21,10 +21,13 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; +using System.Text; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.NRefactory.Utils; using Mono.Cecil; using Mono.Cecil.Cil; +using Mono.CSharp; using Cecil = Mono.Cecil; namespace ICSharpCode.Decompiler.ILAst diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILInlining.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILInlining.cs index 880592b103..11140ef083 100644 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILInlining.cs +++ b/main/contrib/ICSharpCode.Decompiler/ILAst/ILInlining.cs @@ -50,32 +50,36 @@ namespace ICSharpCode.Decompiler.ILAst AnalyzeNode(method); } - void AnalyzeNode(ILNode node) + /// <summary> + /// For each variable reference, adds <paramref name="direction"/> to the num* dicts. + /// Direction will be 1 for analysis, and -1 when removing a node from analysis. + /// </summary> + void AnalyzeNode(ILNode node, int direction = 1) { ILExpression expr = node as ILExpression; if (expr != null) { ILVariable locVar = expr.Operand as ILVariable; if (locVar != null) { if (expr.Code == ILCode.Stloc) { - numStloc[locVar] = numStloc.GetOrDefault(locVar) + 1; + numStloc[locVar] = numStloc.GetOrDefault(locVar) + direction; } else if (expr.Code == ILCode.Ldloc) { - numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + 1; + numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + direction; } else if (expr.Code == ILCode.Ldloca) { - numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + 1; + numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + direction; } else { throw new NotSupportedException(expr.Code.ToString()); } } foreach (ILExpression child in expr.Arguments) - AnalyzeNode(child); + AnalyzeNode(child, direction); } else { var catchBlock = node as ILTryCatchBlock.CatchBlock; if (catchBlock != null && catchBlock.ExceptionVariable != null) { - numStloc[catchBlock.ExceptionVariable] = numStloc.GetOrDefault(catchBlock.ExceptionVariable) + 1; + numStloc[catchBlock.ExceptionVariable] = numStloc.GetOrDefault(catchBlock.ExceptionVariable) + direction; } foreach (ILNode child in node.GetChildren()) - AnalyzeNode(child); + AnalyzeNode(child, direction); } } @@ -194,6 +198,7 @@ namespace ICSharpCode.Decompiler.ILAst // The variable is never loaded if (inlinedExpression.HasNoSideEffects()) { // Remove completely + AnalyzeNode(body[pos], -1); body.RemoveAt(pos); return true; } else if (inlinedExpression.CanBeExpressionStatement() && v.IsGenerated) { @@ -334,6 +339,7 @@ namespace ICSharpCode.Decompiler.ILAst case ILCode.Stfld: case ILCode.Ldfld: case ILCode.Ldflda: + case ILCode.Await: return true; } } diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/PatternMatching.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/PatternMatching.cs index 31fcf62b15..441088b903 100644 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/PatternMatching.cs +++ b/main/contrib/ICSharpCode.Decompiler/ILAst/PatternMatching.cs @@ -167,5 +167,11 @@ namespace ICSharpCode.Decompiler.ILAst ILVariable v; return node.Match(ILCode.Stloc, out v, out expr) && v == expectedVar; } + + public static bool MatchLdcI4(this ILNode node, int expectedValue) + { + int v; + return node.Match(ILCode.Ldc_I4, out v) && v == expectedValue; + } } } diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/StateRange.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/StateRange.cs index 4a55e1d0b6..3a3dfc2f34 100644 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/StateRange.cs +++ b/main/contrib/ICSharpCode.Decompiler/ILAst/StateRange.cs @@ -137,23 +137,25 @@ namespace ICSharpCode.Decompiler.ILAst internal DefaultDictionary<ILNode, StateRange> ranges; SymbolicEvaluationContext evalContext; - internal Dictionary<MethodDefinition, Interval> finallyMethodToStateInterval; // used only for IteratorDispose + internal Dictionary<MethodDefinition, StateRange> finallyMethodToStateRange; // used only for IteratorDispose /// <summary> /// Initializes the state range logic: /// Clears 'ranges' and sets 'ranges[entryPoint]' to the full range (int.MinValue to int.MaxValue) /// </summary> - public StateRangeAnalysis(ILNode entryPoint, StateRangeAnalysisMode mode, FieldDefinition stateField) + public StateRangeAnalysis(ILNode entryPoint, StateRangeAnalysisMode mode, FieldDefinition stateField, ILVariable cachedStateVar = null) { this.mode = mode; this.stateField = stateField; if (mode == StateRangeAnalysisMode.IteratorDispose) { - finallyMethodToStateInterval = new Dictionary<MethodDefinition, Interval>(); + finallyMethodToStateRange = new Dictionary<MethodDefinition, StateRange>(); } ranges = new DefaultDictionary<ILNode, StateRange>(n => new StateRange()); ranges[entryPoint] = new StateRange(int.MinValue, int.MaxValue); evalContext = new SymbolicEvaluationContext(stateField); + if (cachedStateVar != null) + evalContext.AddStateVariable(cachedStateVar); } public int AssignStateRanges(List<ILNode> body, int bodyLength) @@ -213,7 +215,7 @@ namespace ICSharpCode.Decompiler.ILAst break; case ILCode.Brtrue: { - SymbolicValue val = evalContext.Eval(expr.Arguments[0]); + SymbolicValue val = evalContext.Eval(expr.Arguments[0]).AsBool(); if (val.Type == SymbolicValueType.StateEquals) { ranges[(ILLabel)expr.Operand].UnionWith(nodeRange, val.Constant, val.Constant); StateRange nextRange = ranges[body[i + 1]]; @@ -249,9 +251,9 @@ namespace ICSharpCode.Decompiler.ILAst // in some cases (e.g. foreach over array) the C# compiler produces a finally method outside of try-finally blocks if (mode == StateRangeAnalysisMode.IteratorDispose) { MethodDefinition mdef = (expr.Operand as MethodReference).ResolveWithinSameModule(); - if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef)) + if (mdef == null || finallyMethodToStateRange.ContainsKey(mdef)) throw new SymbolicAnalysisFailedException(); - finallyMethodToStateInterval.Add(mdef, nodeRange.ToEnclosingInterval()); + finallyMethodToStateRange.Add(mdef, nodeRange); break; } else { goto default; diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs index 98255f2f2d..5fb40acf92 100644 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs +++ b/main/contrib/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs @@ -67,7 +67,16 @@ namespace ICSharpCode.Decompiler.ILAst this.Type = type; this.Constant = constant; } - + + public SymbolicValue AsBool() + { + if (Type == SymbolicValueType.State) { + // convert state integer to bool: + // if (state + c) = if (state + c != 0) = if (state != -c) + return new SymbolicValue(SymbolicValueType.StateInEquals, unchecked(-Constant)); + } + return this; + } public override string ToString() { return string.Format("[SymbolicValue {0}: {1}]", this.Type, this.Constant); @@ -133,7 +142,7 @@ namespace ICSharpCode.Decompiler.ILAst // bool: (state == right.Constant - left.Constant) return new SymbolicValue(expr.Code == ILCode.Ceq ? SymbolicValueType.StateEquals : SymbolicValueType.StateInEquals, unchecked(right.Constant - left.Constant)); case ILCode.LogicNot: - SymbolicValue val = Eval(expr.Arguments[0]); + SymbolicValue val = Eval(expr.Arguments[0]).AsBool(); if (val.Type == SymbolicValueType.StateEquals) return new SymbolicValue(SymbolicValueType.StateInEquals, val.Constant); else if (val.Type == SymbolicValueType.StateInEquals) @@ -141,7 +150,7 @@ namespace ICSharpCode.Decompiler.ILAst else return Failed(); default: - return Failed(); + return Failed(); } } } diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index b050353ab4..6ed1f4dc05 100644 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/main/contrib/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -450,7 +450,7 @@ namespace ICSharpCode.Decompiler.ILAst return (TypeReference)expr.Operand; case ILCode.Localloc: if (forceInferChildren) { - InferTypeForExpression(expr.Arguments[0], typeSystem.Int32); + InferTypeForExpression(expr.Arguments[0], null); } if (expectedType is PointerType) return expectedType; @@ -539,6 +539,8 @@ namespace ICSharpCode.Decompiler.ILAst if (forceInferChildren) InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); TypeReference type = NumericPromotion(InferTypeForExpression(expr.Arguments[0], null)); + if (type == null) + return null; TypeReference expectedInputType = null; switch (type.MetadataType) { case MetadataType.Int32: @@ -827,7 +829,7 @@ namespace ICSharpCode.Decompiler.ILAst case ILCode.Await: { TypeReference taskType = InferTypeForExpression(expr.Arguments[0], null); - if (taskType.Name == "Task`1" && taskType.IsGenericInstance && taskType.Namespace == "System.Threading.Tasks") { + if (taskType != null && taskType.Name == "Task`1" && taskType.IsGenericInstance && taskType.Namespace == "System.Threading.Tasks") { return ((GenericInstanceType)taskType).GenericArguments[0]; } return null; @@ -1011,7 +1013,7 @@ namespace ICSharpCode.Decompiler.ILAst TypeReference leftPreferred = DoInferTypeForExpression(left, expectedType); if (leftPreferred is PointerType) { left.InferredType = left.ExpectedType = leftPreferred; - InferTypeForExpression(right, typeSystem.IntPtr); + InferTypeForExpression(right, null); return leftPreferred; } if (IsEnum(leftPreferred)) { @@ -1022,7 +1024,7 @@ namespace ICSharpCode.Decompiler.ILAst } TypeReference rightPreferred = DoInferTypeForExpression(right, expectedType); if (rightPreferred is PointerType) { - InferTypeForExpression(left, typeSystem.IntPtr); + InferTypeForExpression(left, null); right.InferredType = right.ExpectedType = rightPreferred; return rightPreferred; } @@ -1042,7 +1044,10 @@ namespace ICSharpCode.Decompiler.ILAst TypeReference leftPreferred = DoInferTypeForExpression(left, expectedType); if (leftPreferred is PointerType) { left.InferredType = left.ExpectedType = leftPreferred; - InferTypeForExpression(right, typeSystem.IntPtr); + TypeReference rightPreferred = InferTypeForExpression(right, null); + // subtracting two pointers is not a pointer + if (rightPreferred is PointerType) + return typeSystem.IntPtr; return leftPreferred; } if (IsEnum(leftPreferred)) { diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs index b4b6183bf8..2da165f610 100644 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs +++ b/main/contrib/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs @@ -312,7 +312,7 @@ namespace ICSharpCode.Decompiler.ILAst // This is (int.MinValue, int.MaxValue) for the first instruction. // These ranges are propagated depending on the conditional jumps performed by the code. - Dictionary<MethodDefinition, Interval> finallyMethodToStateInterval; + Dictionary<MethodDefinition, StateRange> finallyMethodToStateRange; void ConstructExceptionTable() { @@ -321,11 +321,11 @@ namespace ICSharpCode.Decompiler.ILAst var rangeAnalysis = new StateRangeAnalysis(ilMethod.Body[0], StateRangeAnalysisMode.IteratorDispose, stateField); rangeAnalysis.AssignStateRanges(ilMethod.Body, ilMethod.Body.Count); - finallyMethodToStateInterval = rangeAnalysis.finallyMethodToStateInterval; + finallyMethodToStateRange = rangeAnalysis.finallyMethodToStateRange; // Now look at the finally blocks: foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) { - Interval interval = rangeAnalysis.ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval(); + var range = rangeAnalysis.ranges[tryFinally.TryBlock.Body[0]]; var finallyBody = tryFinally.FinallyBlock.Body; if (finallyBody.Count != 2) throw new SymbolicAnalysisFailedException(); @@ -338,9 +338,9 @@ namespace ICSharpCode.Decompiler.ILAst throw new SymbolicAnalysisFailedException(); MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference); - if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef)) + if (mdef == null || finallyMethodToStateRange.ContainsKey(mdef)) throw new SymbolicAnalysisFailedException(); - finallyMethodToStateInterval.Add(mdef, interval); + finallyMethodToStateRange.Add(mdef, range); } rangeAnalysis = null; } @@ -430,10 +430,9 @@ namespace ICSharpCode.Decompiler.ILAst bodyLength--; // don't conside the stloc instruction to be part of the body } - // verify that the last element in the body is a label pointing to the 'ret(false)' + // The last element in the body usually is a label pointing to the 'ret(false)' returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel; - if (returnFalseLabel == null) - throw new SymbolicAnalysisFailedException(); + // Note: in Roslyn-compiled code, returnFalseLabel may be null. var rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.IteratorMoveNext, stateField); int pos = rangeAnalysis.AssignStateRanges(body, bodyLength); @@ -485,7 +484,7 @@ namespace ICSharpCode.Decompiler.ILAst throw new SymbolicAnalysisFailedException(); int val = (int)expr.Arguments[0].Operand; if (val == 0) { - newBody.Add(MakeGoTo(returnFalseLabel)); + newBody.Add(new ILExpression(ILCode.YieldBreak, null)); } else if (val == 1) { newBody.Add(MakeGoTo(labels, currentState)); } else { @@ -497,7 +496,7 @@ namespace ICSharpCode.Decompiler.ILAst // handle direct return (e.g. in release builds) int val = (int)expr.Arguments[0].Operand; if (val == 0) { - newBody.Add(MakeGoTo(returnFalseLabel)); + newBody.Add(new ILExpression(ILCode.YieldBreak, null)); } else if (val == 1) { newBody.Add(MakeGoTo(labels, currentState)); } else { @@ -507,21 +506,21 @@ namespace ICSharpCode.Decompiler.ILAst MethodDefinition method = GetMethodDefinition(expr.Operand as MethodReference); if (method == null) throw new SymbolicAnalysisFailedException(); - Interval interval; + StateRange stateRange; if (method == disposeMethod) { // Explicit call to dispose is used for "yield break;" within the method. ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression; if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnFalseLabel) throw new SymbolicAnalysisFailedException(); - newBody.Add(MakeGoTo(returnFalseLabel)); - } else if (finallyMethodToStateInterval.TryGetValue(method, out interval)) { + newBody.Add(new ILExpression(ILCode.YieldBreak, null)); + } else if (finallyMethodToStateRange.TryGetValue(method, out stateRange)) { // Call to Finally-method - int index = stateChanges.FindIndex(ss => ss.NewState >= interval.Start && ss.NewState <= interval.End); + int index = stateChanges.FindIndex(ss => stateRange.Contains(ss.NewState)); if (index < 0) throw new SymbolicAnalysisFailedException(); ILLabel label = new ILLabel(); - label.Name = "JumpOutOfTryFinally" + interval.Start + "_" + interval.End; + label.Name = "JumpOutOfTryFinally" + stateChanges[index].NewState; newBody.Add(new ILExpression(ILCode.Leave, label)); SetState stateChange = stateChanges[index]; @@ -544,6 +543,7 @@ namespace ICSharpCode.Decompiler.ILAst ILExpression MakeGoTo(ILLabel targetLabel) { + Debug.Assert(targetLabel != null); if (targetLabel == returnFalseLabel) return new ILExpression(ILCode.YieldBreak, null); else diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs b/main/contrib/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs index 552d092ee5..5cb49aad73 100644 --- a/main/contrib/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs +++ b/main/contrib/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs @@ -87,7 +87,8 @@ namespace ICSharpCode.Decompiler.Tests { if(String.IsNullOrWhiteSpace(s)) return true; - return s.Trim().StartsWith("//"); + s = s.Trim(); + return s.StartsWith("//") || s.StartsWith("#"); // Also ignore #pragmas for warning suppression } public static string ConcatLines(IEnumerable<string> lines) diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs b/main/contrib/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs index 8205042a97..659bb2be68 100644 --- a/main/contrib/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs +++ b/main/contrib/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs @@ -35,10 +35,8 @@ namespace ICSharpCode.Decompiler.Tests { protected static void ValidateFileRoundtrip(string samplesFileName) { - var lines = File.ReadAllLines(Path.Combine(@"..\..\Tests", samplesFileName)); - var testCode = RemoveIgnorableLines(lines); - var decompiledTestCode = RoundtripCode(testCode); - CodeAssert.AreEqual(testCode, decompiledTestCode); + var fullPath = Path.Combine(@"..\..\Tests", samplesFileName); + AssertRoundtripCode(fullPath); } static string RemoveIgnorableLines(IEnumerable<string> lines) @@ -46,29 +44,27 @@ namespace ICSharpCode.Decompiler.Tests return CodeSampleFileParser.ConcatLines(lines.Where(l => !CodeSampleFileParser.IsCommentOrBlank(l))); } - /// <summary> - /// Compiles and decompiles a source code. - /// </summary> - /// <param name="code">The source code to copile.</param> - /// <returns>The decompilation result of compiled source code.</returns> - static string RoundtripCode(string code) + protected static void AssertRoundtripCode(string fileName, bool optimize = false, bool useDebug = false, int compilerVersion = 4) { - DecompilerSettings settings = new DecompilerSettings(); - settings.FullyQualifyAmbiguousTypeNames = false; - AssemblyDefinition assembly = Compile(code); - AstBuilder decompiler = new AstBuilder(new DecompilerContext(assembly.MainModule) { Settings = settings }); + var code = RemoveIgnorableLines(File.ReadLines(fileName)); + AssemblyDefinition assembly = CompileLegacy(code, optimize, useDebug, compilerVersion); + + AstBuilder decompiler = new AstBuilder(new DecompilerContext(assembly.MainModule)); decompiler.AddAssembly(assembly); new Helpers.RemoveCompilerAttribute().Run(decompiler.SyntaxTree); + StringWriter output = new StringWriter(); decompiler.GenerateCode(new PlainTextOutput(output)); - return output.ToString(); + CodeAssert.AreEqual(code, output.ToString()); } - static AssemblyDefinition Compile(string code) + protected static AssemblyDefinition CompileLegacy(string code, bool optimize, bool useDebug, int compilerVersion) { - CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v4.0" } }); + CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v" + new Version(compilerVersion, 0) } }); CompilerParameters options = new CompilerParameters(); - options.ReferencedAssemblies.Add("System.Core.dll"); + options.CompilerOptions = "/unsafe /o" + (optimize ? "+" : "-") + (useDebug ? " /debug" : ""); + if (compilerVersion >= 4) + options.ReferencedAssemblies.Add("System.Core.dll"); CompilerResults results = provider.CompileAssemblyFromSource(options, code); try { diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs b/main/contrib/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs index 5d5071f676..70c00fc655 100644 --- a/main/contrib/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs +++ b/main/contrib/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs @@ -62,6 +62,22 @@ public static class DelegateConstruction } return null; } + + public void LambdaInForLoop() + { + for (int i = 0; i < 100000; i++) { + Bar(() => Foo()); + } + } + + public int Foo() + { + return 0; + } + + public void Bar(Func<int> f) + { + } } public static void Test(this string a) diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/main/contrib/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index 69598ac73f..8bb4934e78 100644 --- a/main/contrib/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/main/contrib/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -67,6 +67,7 @@ <Compile Include="CallOverloadedMethod.cs" /> <Compile Include="CheckedUnchecked.cs" /> <Compile Include="ControlFlow.cs" /> + <Compile Include="DoubleConstants.cs" /> <Compile Include="ExpressionTrees.cs" /> <None Include="IL\SequenceOfNestedIfs.Output.cs" /> <Compile Include="IL\ILTests.cs" /> @@ -74,6 +75,7 @@ <Compile Include="CustomShortCircuitOperators.cs" /> <Compile Include="Helpers\CodeAssert.cs" /> <Compile Include="IncrementDecrement.cs" /> + <Compile Include="Lock.cs" /> <Compile Include="PInvoke.cs" /> <Compile Include="QueryExpressions.cs" /> <Compile Include="Switch.cs" /> @@ -126,11 +128,12 @@ <Name>ICSharpCode.Decompiler</Name> </ProjectReference> </ItemGroup> + <ItemGroup /> <ItemGroup> - <Folder Include="IL" /> + <None Include="BooleanConsumedAsInteger.il" /> </ItemGroup> <ItemGroup> - <None Include="BooleanConsumedAsInteger.il" /> + <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" /> </Project>
\ No newline at end of file diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/TestRunner.cs b/main/contrib/ICSharpCode.Decompiler/Tests/TestRunner.cs index 009a3c6c17..215725b0a6 100644 --- a/main/contrib/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/main/contrib/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -32,7 +32,7 @@ using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests { [TestFixture] - public class TestRunner + public class TestRunner : DecompilerTestBase { [Test] public void Async() @@ -67,7 +67,8 @@ namespace ICSharpCode.Decompiler.Tests [Test] public void ExceptionHandling() { - TestFile(@"..\..\Tests\ExceptionHandling.cs", optimize: false); + AssertRoundtripCode(@"..\..\Tests\ExceptionHandling.cs", optimize: false); + AssertRoundtripCode(@"..\..\Tests\ExceptionHandling.cs", optimize: false); } [Test] @@ -85,7 +86,14 @@ namespace ICSharpCode.Decompiler.Tests [Test] public void ControlFlowWithDebug() { - TestFile(@"..\..\Tests\ControlFlow.cs", optimize: false, useDebug: true); + AssertRoundtripCode(@"..\..\Tests\ControlFlow.cs", optimize: false, useDebug: true); + AssertRoundtripCode(@"..\..\Tests\ControlFlow.cs", optimize: false, useDebug: true); + } + + [Test] + public void DoubleConstants() + { + TestFile(@"..\..\Tests\DoubleConstants.cs"); } [Test] @@ -107,6 +115,13 @@ namespace ICSharpCode.Decompiler.Tests } [Test] + public void Lock() + { + //TestFile(@"..\..\Tests\Lock.cs", compilerVersion: 2); + TestFile(@"..\..\Tests\Lock.cs", compilerVersion: 4); + } + + [Test] public void Loops() { TestFile(@"..\..\Tests\Loops.cs"); @@ -172,44 +187,12 @@ namespace ICSharpCode.Decompiler.Tests TestFile(@"..\..\Tests\TypeAnalysisTests.cs"); } - static void TestFile(string fileName, bool useDebug = false) - { - TestFile(fileName, false, useDebug); - TestFile(fileName, true, useDebug); - } - - static void TestFile(string fileName, bool optimize, bool useDebug = false) + static void TestFile(string fileName, bool useDebug = false, int compilerVersion = 4) { - string code = File.ReadAllText(fileName); - AssemblyDefinition assembly = Compile(code, optimize, useDebug); - AstBuilder decompiler = new AstBuilder(new DecompilerContext(assembly.MainModule)); - decompiler.AddAssembly(assembly); - new Helpers.RemoveCompilerAttribute().Run(decompiler.SyntaxTree); - StringWriter output = new StringWriter(); - decompiler.GenerateCode(new PlainTextOutput(output)); - CodeAssert.AreEqual(code, output.ToString()); - } - - static AssemblyDefinition Compile(string code, bool optimize, bool useDebug) - { - CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v4.0" } }); - CompilerParameters options = new CompilerParameters(); - options.CompilerOptions = "/unsafe /o" + (optimize ? "+" : "-") + (useDebug ? " /debug": ""); - options.ReferencedAssemblies.Add("System.Core.dll"); - CompilerResults results = provider.CompileAssemblyFromSource(options, code); - try { - if (results.Errors.Count > 0) { - StringBuilder b = new StringBuilder("Compiler error:"); - foreach (var error in results.Errors) { - b.AppendLine(error.ToString()); - } - throw new Exception(b.ToString()); - } - return AssemblyDefinition.ReadAssembly(results.PathToAssembly); - } finally { - File.Delete(results.PathToAssembly); - results.TempFiles.Delete(); - } + AssertRoundtripCode(fileName, optimize: false, useDebug: useDebug, compilerVersion: compilerVersion); + AssertRoundtripCode(fileName, optimize: true, useDebug: useDebug, compilerVersion: compilerVersion); + AssertRoundtripCode(fileName, optimize: false, useDebug: useDebug, compilerVersion: compilerVersion); + AssertRoundtripCode(fileName, optimize: true, useDebug: useDebug, compilerVersion: compilerVersion); } } } diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs index 72db7aa465..4a85b9f88a 100644 --- a/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs +++ b/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs @@ -679,7 +679,9 @@ namespace HideMembers3 } public class J2 : J { +#pragma warning disable 0108 // Deliberate bad code for test case public int get_P; +#pragma warning restore 0108 } } //$$ HideMembers4 diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/UnsafeCode.cs b/main/contrib/ICSharpCode.Decompiler/Tests/UnsafeCode.cs index 66fb29529b..cce6d525c7 100644 --- a/main/contrib/ICSharpCode.Decompiler/Tests/UnsafeCode.cs +++ b/main/contrib/ICSharpCode.Decompiler/Tests/UnsafeCode.cs @@ -122,7 +122,47 @@ public class UnsafeCode } return this.PointerReferenceExpression((double*)ptr); } - + + public unsafe int* PointerArithmetic(int* p) + { + return p + 2; + } + + public unsafe byte* PointerArithmetic2(long* p, int y, int x) + { + return (byte*)((short*)p + (y * x)); + } + + public unsafe long* PointerArithmetic3(long* p) + { + return (long*)((byte*)p + 3); + } + + public unsafe long* PointerArithmetic4(void* p) + { + return (long*)((byte*)p + 3); + } + + public unsafe int PointerArithmetic5(void* p, byte* q, int i) + { + return (int)(q[i] + *(byte*)p); + } + + public unsafe int PointerSubtraction(long* p, long* q) + { + return (int)((long)(p - q)); + } + + public unsafe int PointerSubtraction2(long* p, short* q) + { + return (int)((long)((byte*)p - (byte*)q)); + } + + public unsafe int PointerSubtraction3(void* p, void* q) + { + return (int)((long)((byte*)p - (byte*)q)); + } + unsafe ~UnsafeCode() { this.PassPointerAsRefParameter(this.NullPointer); diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/ValueTypes.cs b/main/contrib/ICSharpCode.Decompiler/Tests/ValueTypes.cs index b3aa644786..1493cff4c9 100644 --- a/main/contrib/ICSharpCode.Decompiler/Tests/ValueTypes.cs +++ b/main/contrib/ICSharpCode.Decompiler/Tests/ValueTypes.cs @@ -168,4 +168,21 @@ public static class ValueTypes Console.WriteLine("true"); } } + + public static void CompareNotEqual0IsReallyNotEqual(IComparable<int> a) + { + if (a.CompareTo(0) != 0) + { + Console.WriteLine("true"); + } + } + + public static void CompareEqual0IsReallyEqual(IComparable<int> a) + { + if (a.CompareTo(0) == 0) + { + Console.WriteLine("true"); + } + } + } |