From 6e89be0c1970091167e888c1f3565e62c215fd75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Fri, 14 Aug 2015 07:19:35 +0200 Subject: Fixed 'Bug 33024 - Decompiler fails on most classes' --- .../ICSharpCode.Decompiler/Ast/Annotations.cs | 4 +- .../ICSharpCode.Decompiler/Ast/AstBuilder.cs | 30 ++- .../Ast/AstMethodBodyBuilder.cs | 104 ++++------ .../ICSharpCode.Decompiler/Ast/TextTokenWriter.cs | 28 ++- .../Ast/Transforms/DeclareVariables.cs | 31 +++ .../Ast/Transforms/DelegateConstruction.cs | 14 +- .../Ast/Transforms/ExpressionTreeConverter.cs | 2 +- .../Ast/Transforms/IntroduceUnsafeModifier.cs | 6 +- .../Ast/Transforms/IntroduceUsingDeclarations.cs | 23 +-- .../Ast/Transforms/PatternStatementTransform.cs | 92 ++++++++- .../Ast/TypesHierarchyHelpers.cs | 40 ++-- .../ICSharpCode.Decompiler/CecilExtensions.cs | 47 +++++ .../ICSharpCode.Decompiler/DecompilerException.cs | 2 +- .../ICSharpCode.Decompiler/DecompilerSettings.cs | 16 ++ .../Disassembler/DisassemblerHelpers.cs | 8 + .../Disassembler/MethodBodyDisassembler.cs | 4 +- .../Disassembler/ReflectionDisassembler.cs | 22 +- .../ICSharpCode.Decompiler.csproj | 12 +- .../ILAst/AsyncDecompiler.cs | 99 ++++++--- .../ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs | 8 +- .../ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs | 222 +++++++++++++++++++-- .../ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 3 + .../ICSharpCode.Decompiler/ILAst/ILInlining.cs | 20 +- .../ILAst/PatternMatching.cs | 6 + .../ICSharpCode.Decompiler/ILAst/StateRange.cs | 14 +- .../ILAst/SymbolicExecution.cs | 15 +- .../ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 15 +- .../ILAst/YieldReturnDecompiler.cs | 30 +-- .../Tests/CodeSampleFileParser.cs | 3 +- .../Tests/DecompilerTestBase.cs | 32 ++- .../Tests/DelegateConstruction.cs | 16 ++ .../Tests/ICSharpCode.Decompiler.Tests.csproj | 7 +- .../ICSharpCode.Decompiler/Tests/TestRunner.cs | 63 +++--- .../Tests/Types/S_TypeMemberDeclarations.cs | 2 + .../ICSharpCode.Decompiler/Tests/UnsafeCode.cs | 42 +++- .../ICSharpCode.Decompiler/Tests/ValueTypes.cs | 17 ++ 36 files changed, 815 insertions(+), 284 deletions(-) (limited to 'main/contrib') 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 class if (typeDef.Name == "") 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. /// Block for the method body public static BlockStatement CreateMethodBody(MethodDefinition methodDef, - DecompilerContext context, - IEnumerable parameters = null) + DecompilerContext context, + IEnumerable 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() equals v.OriginalParameter - select new { p, v.Name })) + join v in astBuilder.Parameters on p.Annotation() 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 } } - /// - /// Divides expr by the size of 'type'. - /// - 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 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 nodeStack = new Stack(); 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(); } + 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 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().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 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("enter").Single(); + exit = m2.Get("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("enter").Single(); + exit = m2.Get("exit").Single(); + return m1.Get("variable").Single().Identifier == m2.Get("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("variable").Single().Identifier == m2.Get("flag").Single().Identifier) { - Expression enter = m2.Get("enter").Single(); - IdentifierExpression exit = m2.Get("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("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(); } + + /// + /// checks if the given TypeReference is one of the following types: + /// [sbyte, short, int, long, IntPtr] + /// + 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; + } + + /// + /// 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] + /// + 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 /// @@ -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 { /// - /// Desctiption of DecompilerException. + /// Description of DecompilerException. /// 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; + + /// + /// Gets/Sets whether to use assignment expressions such as in while ((count = Do()) != 0) ; + /// + public bool MakeAssignmentExpressions { + get { return makeAssignmentExpressions; } + set { + if (makeAssignmentExpressions != value) { + makeAssignmentExpressions = value; + OnPropertyChanged("MakeAssignmentExpressions"); + } + } + } + bool alwaysGenerateExceptionVariableForCatchBlocks = false; /// @@ -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 /// 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 @@ + @@ -97,6 +98,8 @@ + + @@ -109,6 +112,8 @@ + + @@ -116,11 +121,6 @@ - - - - - @@ -147,4 +147,4 @@ - + \ 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 fieldToParameterMap = new Dictionary(); + 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 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 ConvertFinally(List body) { List newBody = new List(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 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()) { 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()) { - 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()) { + 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()) { + List 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 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) + /// + /// For each variable reference, adds to the num* dicts. + /// Direction will be 1 for analysis, and -1 when removing a node from analysis. + /// + 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 ranges; SymbolicEvaluationContext evalContext; - internal Dictionary finallyMethodToStateInterval; // used only for IteratorDispose + internal Dictionary finallyMethodToStateRange; // used only for IteratorDispose /// /// Initializes the state range logic: /// Clears 'ranges' and sets 'ranges[entryPoint]' to the full range (int.MinValue to int.MaxValue) /// - 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(); + finallyMethodToStateRange = new Dictionary(); } ranges = new DefaultDictionary(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 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 finallyMethodToStateInterval; + Dictionary 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()) { - 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 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 lines) @@ -46,29 +44,27 @@ namespace ICSharpCode.Decompiler.Tests return CodeSampleFileParser.ConcatLines(lines.Where(l => !CodeSampleFileParser.IsCommentOrBlank(l))); } - /// - /// Compiles and decompiles a source code. - /// - /// The source code to copile. - /// The decompilation result of compiled source code. - 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 { { "CompilerVersion", "v4.0" } }); + CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary { { "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 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 @@ + @@ -74,6 +75,7 @@ + @@ -126,11 +128,12 @@ ICSharpCode.Decompiler + - + - + \ 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] @@ -106,6 +114,13 @@ namespace ICSharpCode.Decompiler.Tests TestFile(@"..\..\Tests\LiftedOperators.cs"); } + [Test] + public void Lock() + { + //TestFile(@"..\..\Tests\Lock.cs", compilerVersion: 2); + TestFile(@"..\..\Tests\Lock.cs", compilerVersion: 4); + } + [Test] public void Loops() { @@ -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 { { "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 a) + { + if (a.CompareTo(0) != 0) + { + Console.WriteLine("true"); + } + } + + public static void CompareEqual0IsReallyEqual(IComparable a) + { + if (a.CompareTo(0) == 0) + { + Console.WriteLine("true"); + } + } + } -- cgit v1.2.3 From 621faabd6d7aa6cffa085b9e0abd8b865c01cd09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Fri, 14 Aug 2015 07:21:52 +0200 Subject: [Decompiler] Added missing files. The decompiler tests are unused in monodevelop. --- .../Properties/AssemblyInfo.template.cs | 27 + main/contrib/ICSharpCode.Decompiler/Tests/Async.cs | 155 ++++ .../ICSharpCode.Decompiler/Tests/ControlFlow.cs | 97 +++ .../Tests/CustomShortCircuitOperators.cs | 88 +++ .../Tests/DoubleConstants.cs | 28 + .../Tests/ExpressionTrees.cs | 370 +++++++++ .../Tests/Helpers/CodeAssert.cs | 110 +++ .../ICSharpCode.Decompiler/Tests/IL/ILTests.cs | 51 ++ .../Tests/IL/SequenceOfNestedIfs.Output.cs | 53 ++ .../Tests/IL/SequenceOfNestedIfs.dll | Bin 0 -> 2560 bytes .../Tests/IL/SequenceOfNestedIfs.il | 140 ++++ .../ICSharpCode.Decompiler/Tests/IL/StackTests.exe | Bin 0 -> 2048 bytes .../ICSharpCode.Decompiler/Tests/IL/StackTests.il | 132 ++++ .../Tests/LiftedOperators.cs | 830 +++++++++++++++++++++ main/contrib/ICSharpCode.Decompiler/Tests/Lock.cs | 38 + .../ICSharpCode.Decompiler/Tests/PInvoke.cs | 96 +++ .../Tests/QueryExpressions.cs | 188 +++++ .../Tests/TypeAnalysisTests.cs | 153 ++++ .../Tests/Types/S_TypeDeclarations.cs | 17 + .../Tests/UndocumentedExpressions.cs | 41 + 20 files changed, 2614 insertions(+) create mode 100644 main/contrib/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/Async.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/ControlFlow.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/DoubleConstants.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/ExpressionTrees.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/IL/ILTests.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.Output.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.dll create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.il create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.exe create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.il create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/LiftedOperators.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/Lock.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/PInvoke.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/QueryExpressions.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs create mode 100644 main/contrib/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs (limited to 'main/contrib') diff --git a/main/contrib/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs b/main/contrib/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs new file mode 100644 index 0000000000..3dba481ae1 --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs @@ -0,0 +1,27 @@ +#region Using directives + +using System; +using System.Resources; +using System.Reflection; +using System.Runtime.InteropServices; + +#endregion + +[assembly: AssemblyTitle("ICSharpCode.Decompiler")] +[assembly: AssemblyDescription("IL decompiler engine")] +[assembly: AssemblyCompany("ic#code")] +[assembly: AssemblyProduct("ILSpy")] +[assembly: AssemblyCopyright("Copyright 2011-2014 AlphaSierraPapa for the SharpDevelop Team")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. +[assembly: ComVisible(false)] + +[assembly: AssemblyVersion("$INSERTVERSION$")] +[assembly: AssemblyInformationalVersion("$INSERTVERSION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$-$INSERTSHORTCOMMITHASH$")] +[assembly: NeutralResourcesLanguage("en-US")] + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", + Justification = "AssemblyInformationalVersion does not need to be a parsable version")] diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Async.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Async.cs new file mode 100644 index 0000000000..7629dd5e62 --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/Async.cs @@ -0,0 +1,155 @@ +// Copyright (c) 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. + +#pragma warning disable 1998 +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +public class Async +{ + public async void SimpleVoidMethod() + { + Console.WriteLine("Before"); + await Task.Delay(TimeSpan.FromSeconds(1.0)); + Console.WriteLine("After"); + } + + public async void VoidMethodWithoutAwait() + { + Console.WriteLine("No Await"); + } + + public async void AwaitYield() + { + await Task.Yield(); + } + + public async void AwaitDefaultYieldAwaitable() + { + await default(YieldAwaitable); + } + + public async Task SimpleVoidTaskMethod() + { + Console.WriteLine("Before"); + await Task.Delay(TimeSpan.FromSeconds(1.0)); + Console.WriteLine("After"); + } + + public async Task TaskMethodWithoutAwait() + { + Console.WriteLine("No Await"); + } + + public async Task SimpleBoolTaskMethod() + { + Console.WriteLine("Before"); + await Task.Delay(TimeSpan.FromSeconds(1.0)); + Console.WriteLine("After"); + return true; + } + + public async void TwoAwaitsWithDifferentAwaiterTypes() + { + Console.WriteLine("Before"); + if (await this.SimpleBoolTaskMethod()) + { + await Task.Delay(TimeSpan.FromSeconds(1.0)); + } + Console.WriteLine("After"); + } + + public async void StreamCopyTo(Stream destination, int bufferSize) + { + byte[] array = new byte[bufferSize]; + int count; + while ((count = await destination.ReadAsync(array, 0, array.Length)) != 0) + { + await destination.WriteAsync(array, 0, count); + } + } + + public async void StreamCopyToWithConfigureAwait(Stream destination, int bufferSize) + { + byte[] array = new byte[bufferSize]; + int count; + while ((count = await destination.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0) + { + await destination.WriteAsync(array, 0, count).ConfigureAwait(false); + } + } + + public async void AwaitInLoopCondition() + { + while (await this.SimpleBoolTaskMethod()) + { + Console.WriteLine("Body"); + } + } + + public async Task AwaitInForEach(IEnumerable> elements) + { + int num = 0; + foreach (Task current in elements) + { + num += await current; + } + return num; + } + + public async Task TaskMethodWithoutAwaitButWithExceptionHandling() + { + try + { + using (new StringWriter()) + { + Console.WriteLine("No Await"); + } + } + catch (Exception) + { + Console.WriteLine("Crash"); + } + } + + public async Task NestedAwait(Task> task) + { + return await(await task); + } + + public async Task AwaitWithStack(Task task) + { + Console.WriteLine("A", 1, await task); + } + + public async Task AwaitWithStack2(Task task) + { + if (await this.SimpleBoolTaskMethod()) + { + Console.WriteLine("A", 1, await task); + } + else + { + int num = 1; + Console.WriteLine("A", 1, num); + } + } +} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/ControlFlow.cs b/main/contrib/ICSharpCode.Decompiler/Tests/ControlFlow.cs new file mode 100644 index 0000000000..d83f04a2b1 --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/ControlFlow.cs @@ -0,0 +1,97 @@ +// Copyright (c) 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; + +public static class ControlFlow +{ + public static void EmptyIf(string input, List value, Dictionary _headers) + { + if (value.Contains("test")) + { + } + _headers.Add(2, "result"); + } + + public static void NormalIf(string input, List value, Dictionary _headers) + { + if (value.Contains("test")) + { + _headers.Add(1, "result"); + } + else + { + _headers.Add(1, "else"); + } + _headers.Add(2, "end"); + } + + public static void NormalIf2(string input, List value, Dictionary _headers) + { + if (value.Contains("test")) + { + _headers.Add(1, "result"); + } + _headers.Add(2, "end"); + } + + public static void NormalIf3(string input, List value, Dictionary _headers) + { + if (value.Contains("test")) + { + _headers.Add(1, "result"); + } + else + { + _headers.Add(1, "else"); + } + } + + public static void Test(string input, List value, Dictionary _headers) + { + foreach (string current in value) + { + _headers.Add(0, current); + } + if (value.Contains("test")) + { + _headers.Add(1, "result"); + } + else + { + _headers.Add(1, "else"); + } + } + + public static void CascadingIfElse(bool condition, string input, int index) + { + if (condition) + { + Console.WriteLine("condition"); + } + else if (input == null) + { + Console.WriteLine("condition2"); + } + else if (index > 1) + { + Console.WriteLine("condition3"); + } + } +} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs b/main/contrib/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs new file mode 100644 index 0000000000..5bd0ec516b --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs @@ -0,0 +1,88 @@ +// Copyright (c) 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; + +public static class CustomShortCircuitOperators +{ + // TODO: Restore base class after https://roslyn.codeplex.com/workitem/358 is fixed. + private class C + { + public static bool operator true(CustomShortCircuitOperators.C x) + { + return true; + } + + public static bool operator false(CustomShortCircuitOperators.C x) + { + return false; + } + + public static CustomShortCircuitOperators.C operator &(CustomShortCircuitOperators.C x, CustomShortCircuitOperators.C y) + { + return null; + } + + public static CustomShortCircuitOperators.C operator |(CustomShortCircuitOperators.C x, CustomShortCircuitOperators.C y) + { + return null; + } + + public static bool operator !(CustomShortCircuitOperators.C x) + { + return false; + } + + private static void Main() + { + CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C(); + CustomShortCircuitOperators.C c2 = new CustomShortCircuitOperators.C(); + CustomShortCircuitOperators.C c3 = c && c2; + CustomShortCircuitOperators.C c4 = c || c2; + Console.WriteLine(c3.ToString()); + Console.WriteLine(c4.ToString()); + } + + private static void Test2() + { + CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C(); + if (c && c) + { + Console.WriteLine(c.ToString()); + } + + if (!(c && c)) + { + Console.WriteLine(c.ToString()); + } + } + + private static void Test3() + { + CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C(); + if (c) + { + Console.WriteLine(c.ToString()); + } + if (!c) + { + Console.WriteLine(c.ToString()); + } + } + } +} \ No newline at end of file diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/DoubleConstants.cs b/main/contrib/ICSharpCode.Decompiler/Tests/DoubleConstants.cs new file mode 100644 index 0000000000..2bc20a7730 --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/DoubleConstants.cs @@ -0,0 +1,28 @@ +// Copyright (c) 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; + +public class DoubleConstants +{ + public const double Zero = 0.0; + public const double MinusZero = -0.0; + public const double NaN = double.NaN; + public const double PositiveInfinity = double.PositiveInfinity; + public const double NegativeInfinity = double.NegativeInfinity; +} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/ExpressionTrees.cs b/main/contrib/ICSharpCode.Decompiler/Tests/ExpressionTrees.cs new file mode 100644 index 0000000000..9d325e9e9d --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/ExpressionTrees.cs @@ -0,0 +1,370 @@ +// Copyright (c) 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.Linq.Expressions; +using System.Xml; + +public class ExpressionTrees +{ + class GenericClass + { + public static X StaticField; + public X InstanceField; + public static X StaticProperty { get; set; } + public X InstanceProperty { get; set; } + + public static bool GenericMethod() + { + return false; + } + } + + int field; + + static object ToCode(object x, Expression> expr) + { + return expr; + } + + static object ToCode(object x, Expression> expr) + { + return expr; + } + + static object X() + { + return null; + } + + public void Parameter(bool a) + { + ToCode(X(), () => a); + } + + public void LocalVariable() + { + bool a = true; + ToCode(X(), () => a); + } + + public void LambdaParameter() + { + ToCode(X(), (bool a) => a); + } + + public void AddOperator(int x) + { + ToCode(X(), () => 1 + x + 2); + } + + public void AnonymousClasses() + { + ToCode(X(), () => new { X = 3, A = "a" }); + } + + public void ArrayIndex() + { + ToCode(X(), () => new[] { 3, 4, 5 }[0 + (int)(DateTime.Now.Ticks % 3)]); + } + + public void ArrayLengthAndDoubles() + { + ToCode(X(), () => new[] { 1.0, 2.01, 3.5 }.Concat(new[] { 1.0, 2.0 }).ToArray().Length); + } + + public void AsOperator() + { + ToCode(X(), () => new object() as string); + } + + public void ComplexGenericName() + { + ToCode(X(), () => ((Func)(x => x > 0))(0)); + } + + public void DefaultValue() + { + ToCode(X(), () => new TimeSpan(1, 2, 3) == default(TimeSpan)); + } + + public void EnumConstant() + { + ToCode(X(), () => new object().Equals(MidpointRounding.ToEven)); + } + + public void IndexerAccess() + { + var dict = Enumerable.Range(1, 20).ToDictionary(n => n.ToString()); + ToCode(X(), () => dict["3"] == 3); + } + + public void IsOperator() + { + ToCode(X(), () => new object() is string); + } + + public void ListInitializer() + { + ToCode(X(), () => new Dictionary { { 1, 1 }, { 2, 2 }, { 3, 4 } }.Count == 3); + } + + public void ListInitializer2() + { + ToCode(X(), () => new List(50) { 1, 2, 3 }.Count == 3); + } + + public void ListInitializer3() + { + ToCode(X(), () => new List { 1, 2, 3 }.Count == 3); + } + + public void LiteralCharAndProperty() + { + ToCode(X(), () => new string(' ', 3).Length == 1); + } + + public void CharNoCast() + { + ToCode(X(), () => "abc"[1] == 'b'); + } + + public void StringsImplicitCast() + { + int i = 1; + string x = "X"; + ToCode(X(), () => (("a\n\\b" ?? x) + x).Length == 2 ? false : true && (1m + -i > 0 || false)); + } + + public void NotImplicitCast() + { + byte z = 42; + ToCode(X(), () => ~z == 0); + } + + public void MembersBuiltin() + { + ToCode(X(), () => 1.23m.ToString()); + ToCode(X(), () => AttributeTargets.All.HasFlag((Enum)AttributeTargets.Assembly)); + ToCode(X(), () => "abc".Length == 3); + ToCode(X(), () => 'a'.CompareTo('b') < 0); + } + + public void MembersDefault() + { + ToCode(X(), () => default(DateTime).Ticks == 0); + ToCode(X(), () => default(int[]).Length == 0); + ToCode(X(), () => default(Type).IsLayoutSequential); + ToCode(X(), () => default(List).Count); + ToCode(X(), () => default(int[]).Clone() == null); + ToCode(X(), () => default(Type).IsInstanceOfType(new object())); + ToCode(X(), () => default(List).AsReadOnly()); + } + + public void DoAssert() + { + field = 37; + ToCode(X(), () => field != C()); + ToCode(X(), () => !ReferenceEquals(this, new ExpressionTrees())); + ToCode(X(), () => MyEquals(this) && !MyEquals(default(ExpressionTrees))); + } + + int C() + { + return field + 5; + } + + bool MyEquals(ExpressionTrees other) + { + return other != null && field == other.field; + } + + public void MethodGroupAsExtensionMethod() + { + ToCode(X(), () => (Func)new[] { 2000, 2004, 2008, 2012 }.Any); + } + + public void MethodGroupConstant() + { + ToCode(X(), () => Array.TrueForAll(new[] { 2000, 2004, 2008, 2012 }, DateTime.IsLeapYear)); + + HashSet set = new HashSet(); + ToCode(X(), () => new[] { 2000, 2004, 2008, 2012 }.All(set.Add)); + + Func, bool> sink = f => f(null, null); + ToCode(X(), () => sink(int.Equals)); + } + + public void MultipleCasts() + { + ToCode(X(), () => 1 == (int)(object)1); + } + + public void MultipleDots() + { + ToCode(X(), () => 3.ToString().ToString().Length > 0); + } + + public void NestedLambda() + { + Func, int> call = f => f(); + //no params + ToCode(X(), () => call(() => 42)); + //one param + ToCode(X(), () => new[] { 37, 42 }.Select(x => x * 2)); + //two params + ToCode(X(), () => new[] { 37, 42 }.Select((x, i) => x * 2)); + } + + public void CurriedLambda() + { + ToCode>>(X(), a => b => c => a + b + c); + } + + bool Fizz(Func a) + { + return a(42); + } + + bool Buzz(Func a) + { + return a(42); + } + + bool Fizz(Func a) + { + return a("42"); + } + + public void NestedLambda2() + { + ToCode(X(), () => Fizz(x => x == "a")); + ToCode(X(), () => Fizz(x => x == 37)); + + ToCode(X(), () => Fizz((int x) => true)); + ToCode(X(), () => Buzz(x => true)); + } + + public void NewArrayAndExtensionMethod() + { + ToCode(X(), () => new[] { 1.0, 2.01, 3.5 }.SequenceEqual(new[] { 1.0, 2.01, 3.5 })); + } + + public void NewMultiDimArray() + { + ToCode(X(), () => new int[3, 4].Length == 1); + } + + public void NewObject() + { + ToCode(X(), () => new object() != new object()); + } + + public void NotOperator() + { + bool x = true; + int y = 3; + byte z = 42; + ToCode(X(), () => ~(int)z == 0); + ToCode(X(), () => ~y == 0); + ToCode(X(), () => !x); + } + + public void ObjectInitializers() + { + XmlReaderSettings s = new XmlReaderSettings { + CloseInput = false, + CheckCharacters = false + }; + ToCode(X(), () => new XmlReaderSettings { CloseInput = s.CloseInput, CheckCharacters = s.CheckCharacters }.Equals(s)); + } + + public void Quoted() + { + ToCode(X(), () => (Expression>)((n, s) => s + n.ToString()) != null); + } + + public void Quoted2() + { + ToCode(X(), () => ToCode(X(), () => true).Equals(null)); + } + + public void QuotedWithAnonymous() + { + ToCode(X(), () => new[] { new { X = "a", Y = "b" } }.Select(o => o.X + o.Y).Single()); + } + + public void StaticCall() + { + ToCode(X(), () => Equals(3, 0)); + } + + public void ThisCall() + { + ToCode(X(), () => !Equals(3)); + } + + public void ThisExplicit() + { + ToCode(X(), () => object.Equals(this, 3)); + } + + public void TypedConstant() + { + ToCode(X(), () => new[] { typeof(int), typeof(string) }); + } + + public void StaticCallImplicitCast() + { + ToCode(X(), () => Equals(3, 0)); + } + + public void StaticMembers() + { + ToCode(X(), () => (DateTime.Now > DateTime.Now + TimeSpan.FromMilliseconds(10.001)).ToString() == "False"); + } + + public void Strings() + { + int i = 1; + string x = "X"; + ToCode(X(), () => (("a\n\\b" ?? x) + x).Length == 2 ? false : true && (1m + (decimal)-i > 0m || false)); + } + + public void StringAccessor() + { + ToCode(X(), () => (int)"abc"[1] == 98); + } + + public void GenericClassInstance() + { + ToCode(X(), () => new GenericClass().InstanceField + new GenericClass().InstanceProperty); + } + + public void GenericClassStatic() + { + ToCode(X(), () => GenericClass.StaticField + GenericClass.StaticProperty); + } + + public void InvokeGenericMethod() + { + ToCode(X(), () => GenericClass.GenericMethod()); + } +} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs new file mode 100644 index 0000000000..d06e2db29d --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using DiffLib; +using NUnit.Framework; + +namespace ICSharpCode.Decompiler.Tests.Helpers +{ + public class CodeAssert + { + public static void AreEqual(string input1, string input2) + { + var diff = new StringWriter(); + if (!Compare(input1, input2, diff)) { + Assert.Fail(diff.ToString()); + } + } + + static bool Compare(string input1, string input2, StringWriter diff) + { + var differ = new AlignedDiff( + NormalizeAndSplitCode(input1), + NormalizeAndSplitCode(input2), + new CodeLineEqualityComparer(), + new StringSimilarityComparer(), + new StringAlignmentFilter()); + + bool result = true, ignoreChange; + + int line1 = 0, line2 = 0; + + foreach (var change in differ.Generate()) { + switch (change.Change) { + case ChangeType.Same: + diff.Write("{0,4} {1,4} ", ++line1, ++line2); + diff.Write(" "); + diff.WriteLine(change.Element1); + break; + case ChangeType.Added: + diff.Write(" {1,4} ", line1, ++line2); + result &= ignoreChange = ShouldIgnoreChange(change.Element2); + diff.Write(ignoreChange ? " " : " + "); + diff.WriteLine(change.Element2); + break; + case ChangeType.Deleted: + diff.Write("{0,4} ", ++line1, line2); + result &= ignoreChange = ShouldIgnoreChange(change.Element1); + diff.Write(ignoreChange ? " " : " - "); + diff.WriteLine(change.Element1); + break; + case ChangeType.Changed: + diff.Write("{0,4} ", ++line1, line2); + result = false; + diff.Write("(-) "); + diff.WriteLine(change.Element1); + diff.Write(" {1,4} ", line1, ++line2); + diff.Write("(+) "); + diff.WriteLine(change.Element2); + break; + } + } + + return result; + } + + class CodeLineEqualityComparer : IEqualityComparer + { + private IEqualityComparer baseComparer = EqualityComparer.Default; + + public bool Equals(string x, string y) + { + return baseComparer.Equals( + NormalizeLine(x), + NormalizeLine(y) + ); + } + + public int GetHashCode(string obj) + { + return baseComparer.GetHashCode(NormalizeLine(obj)); + } + } + + private static string NormalizeLine(string line) + { + line = line.Trim(); + var index = line.IndexOf("//"); + if (index >= 0) { + return line.Substring(0, index); + } else if (line.StartsWith("#")) { + return string.Empty; + } else { + return line; + } + } + + private static bool ShouldIgnoreChange(string line) + { + // for the result, we should ignore blank lines and added comments + return NormalizeLine(line) == string.Empty; + } + + private static IEnumerable NormalizeAndSplitCode(string input) + { + return input.Split(new[] { "\r\n", "\n\r", "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries); + } + } +} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/ILTests.cs b/main/contrib/ICSharpCode.Decompiler/Tests/IL/ILTests.cs new file mode 100644 index 0000000000..f06f3108c5 --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/IL/ILTests.cs @@ -0,0 +1,51 @@ +// Copyright (c) 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.IO; +using ICSharpCode.Decompiler.Ast; +using ICSharpCode.Decompiler.Tests.Helpers; +using Mono.Cecil; +using NUnit.Framework; + +namespace ICSharpCode.Decompiler.Tests +{ + [TestFixture] + public class ILTests + { + const string path = "../../Tests/IL"; + + [Test] + public void SequenceOfNestedIfs() + { + Run("SequenceOfNestedIfs.dll", "SequenceOfNestedIfs.Output.cs"); + } + + void Run(string compiledFile, string expectedOutputFile) + { + string expectedOutput = File.ReadAllText(Path.Combine(path, expectedOutputFile)); + var assembly = AssemblyDefinition.ReadAssembly(Path.Combine(path, compiledFile)); + 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(expectedOutput, output.ToString()); + } + } +} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.Output.cs b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.Output.cs new file mode 100644 index 0000000000..754d7dafdc --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.Output.cs @@ -0,0 +1,53 @@ +using System; +[Serializable] +public class Material +{ + public static implicit operator bool(Material m) + { + return m == null; + } +} +[Serializable] +public class SequenceOfNestedIfs +{ + public bool _clear; + public Material _material; + public override bool CheckShader() + { + return false; + } + public override void CreateMaterials() + { + if (!this._clear) + { + if (!this.CheckShader()) + { + return; + } + this._material = new Material(); + } + if (!this._material) + { + if (!this.CheckShader()) + { + return; + } + this._material = new Material(); + } + if (!this._material) + { + if (!this.CheckShader()) + { + return; + } + this._material = new Material(); + } + if (!this._material) + { + if (this.CheckShader()) + { + this._material = new Material(); + } + } + } +} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.dll b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.dll new file mode 100644 index 0000000000..f517d4546d Binary files /dev/null and b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.dll differ diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.il b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.il new file mode 100644 index 0000000000..9c9b749ce4 --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.il @@ -0,0 +1,140 @@ +// Metadata version: v2.0.50727 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 2:0:0:0 +} + +.assembly SequenceOfNestedIfs +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module SequenceOfNestedIfs +// MVID: {DCEC8A87-5679-4EBE-89A3-51274D8B5446} +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x01D60000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public auto ansi serializable beforefieldinit Material + extends [mscorlib]System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method Material::.ctor + + .method public hidebysig specialname static + bool op_Implicit(class Material m) cil managed + { + // Code size 11 (0xb) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0008: ceq + IL_000a: ret + } // end of method Material::op_Implicit + +} // end of class Material + +.class public auto ansi serializable beforefieldinit SequenceOfNestedIfs + extends [mscorlib]System.Object +{ + .field public bool _clear + .field public class Material _material + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method SequenceOfNestedIfs::.ctor + + .method public hidebysig virtual instance bool + CheckShader() cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldc.i4.0 + IL_0001: ret + } // end of method SequenceOfNestedIfs::CheckShader + + .method public hidebysig virtual instance void + CreateMaterials() cil managed + { + // Code size 168 (0xa8) + .maxstack 13 + IL_0000: ldarg.0 + IL_0001: ldfld bool SequenceOfNestedIfs::_clear + IL_0006: brtrue IL_0026 + + IL_000b: ldarg.0 + IL_000c: callvirt instance bool SequenceOfNestedIfs::CheckShader() + IL_0011: brtrue IL_001b + + IL_0016: br IL_00a7 + + IL_001b: ldarg.0 + IL_001c: newobj instance void Material::.ctor() + IL_0021: stfld class Material SequenceOfNestedIfs::_material + IL_0026: ldarg.0 + IL_0027: ldfld class Material SequenceOfNestedIfs::_material + IL_002c: call bool Material::op_Implicit(class Material) + IL_0031: brtrue IL_0051 + + IL_0036: ldarg.0 + IL_0037: callvirt instance bool SequenceOfNestedIfs::CheckShader() + IL_003c: brtrue IL_0046 + + IL_0041: br IL_00a7 + + IL_0046: ldarg.0 + IL_0047: newobj instance void Material::.ctor() + IL_004c: stfld class Material SequenceOfNestedIfs::_material + IL_0051: ldarg.0 + IL_0052: ldfld class Material SequenceOfNestedIfs::_material + IL_0057: call bool Material::op_Implicit(class Material) + IL_005c: brtrue IL_007c + + IL_0061: ldarg.0 + IL_0062: callvirt instance bool SequenceOfNestedIfs::CheckShader() + IL_0067: brtrue IL_0071 + + IL_006c: br IL_00a7 + + IL_0071: ldarg.0 + IL_0072: newobj instance void Material::.ctor() + IL_0077: stfld class Material SequenceOfNestedIfs::_material + IL_007c: ldarg.0 + IL_007d: ldfld class Material SequenceOfNestedIfs::_material + IL_0082: call bool Material::op_Implicit(class Material) + IL_0087: brtrue IL_00a7 + + IL_008c: ldarg.0 + IL_008d: callvirt instance bool SequenceOfNestedIfs::CheckShader() + IL_0092: brtrue IL_009c + + IL_0097: br IL_00a7 + + IL_009c: ldarg.0 + IL_009d: newobj instance void Material::.ctor() + IL_00a2: stfld class Material SequenceOfNestedIfs::_material + IL_00a7: ret + } // end of method SequenceOfNestedIfs::CreateMaterials + +} // end of class SequenceOfNestedIfs \ No newline at end of file diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.exe b/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.exe new file mode 100644 index 0000000000..db6194ad68 Binary files /dev/null and b/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.exe differ diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.il b/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.il new file mode 100644 index 0000000000..51cee37549 --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.il @@ -0,0 +1,132 @@ +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) + .ver 4:0:0:0 +} +.assembly StackTests +{ + .hash algorithm 0x00008004 + .ver 1:0:4059:39717 +} +.module StackTests.exe +.imagebase 0x00400000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000003 // ILONLY 32BITREQUIRED + +.class private auto ansi beforefieldinit StackTests.Program extends [mscorlib]System.Object +{ + .method public hidebysig static void Main(string[] args) cil managed + { + .entrypoint + .maxstack 8 + + ldc.i4.0 + call string StackTests.Program::Test1(bool cond) + call void [mscorlib]System.Console::WriteLine(string) // false + + ldc.i4.1 + call string StackTests.Program::Test1(bool cond) + call void [mscorlib]System.Console::WriteLine(string) // true + + ldc.i4.0 + ldc.i4.0 + ldc.i4.0 + call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) + call void [mscorlib]System.Console::WriteLine(int32) // 11 + + ldc.i4.0 + ldc.i4.1 + ldc.i4.0 + call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) + call void [mscorlib]System.Console::WriteLine(int32) // 21 + + ldc.i4.1 + ldc.i4.1 + ldc.i4.1 + call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) + call void [mscorlib]System.Console::WriteLine(int32) // 32 + + ldc.i4.2 + ldc.i4.1 + ldc.i4.0 + call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2) + call void [mscorlib]System.Console::WriteLine(int32) // 23 + + ret + } + + .method public hidebysig static string Test1(bool cond) cil managed + { + ldarg.0 + brtrue TRUE + + FALSE: + ldstr "false" + br EXIT + + TRUE: + ldstr "true" + + EXIT: + ret + } + + .method public hidebysig static int32 Test2(int32 switch1, int32 br1, int32 br2) cil managed + { + ldarg.0 + switch (ENTRY1, ENTRY2, ENTRY3) + ldc.i4.0 + ret + + ENTRY1: + ldc.i4.1 + br BRANCH1 + + ENTRY2: + ldc.i4.2 + br BRANCH1 + + ENTRY3: + ldc.i4.3 + br BRANCH2 + + BRANCH1: + ldarg.1 + brtrue BRANCH2 + + EXIT1: + ldc.i4 10 + add + ret + + BRANCH2: + ldarg.2 + brtrue.s EXIT3 + + EXIT2: + ldc.i4 20 + add + ret + + EXIT3: + ldc.i4 30 + add + ret + } + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method Program::.ctor + +} // end of class StackTests.Program + + +// ============================================================= diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/LiftedOperators.cs b/main/contrib/ICSharpCode.Decompiler/Tests/LiftedOperators.cs new file mode 100644 index 0000000000..426276ab0a --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/LiftedOperators.cs @@ -0,0 +1,830 @@ +// Copyright (c) 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.InteropServices; + +public static class LiftedOperators +{ + // C# uses 4 different patterns of IL for lifted operators: bool, other primitive types, decimal, other structs. + // Different patterns are used depending on whether both of the operands are nullable or only the left/right operand is nullable. + // Negation must not be pushed through such comparisons because it would change the semantics. + // A comparison used in a condition differs somewhat from a comparison used as a simple value. + + public static void BoolBasic(bool? a, bool? b) + { + if (a == b) + { + Console.WriteLine(); + } + if (a != b) + { + Console.WriteLine(); + } + + if (!(a == b)) + { + Console.WriteLine(); + } + if (!(a != b)) + { + Console.WriteLine(); + } + } + + public static void BoolComplex(bool? a, Func x) + { + if (a == x()) + { + Console.WriteLine(); + } + if (a != x()) + { + Console.WriteLine(); + } + + if (x() == a) + { + Console.WriteLine(); + } + if (x() != a) + { + Console.WriteLine(); + } + + if (!(a == x())) + { + Console.WriteLine(); + } + if (!(a != x())) + { + Console.WriteLine(); + } + if (!(x() == a)) + { + Console.WriteLine(); + } + if (!(x() != a)) + { + Console.WriteLine(); + } + } + + public static void BoolConst(bool? a) + { + if (a == true) + { + Console.WriteLine(); + } + if (a != true) + { + Console.WriteLine(); + } + if (a == false) + { + Console.WriteLine(); + } + if (a != false) + { + Console.WriteLine(); + } + if (a ?? true) + { + Console.WriteLine(); + } + if (a ?? false) + { + Console.WriteLine(); + } + } + + public static void BoolValueBasic(bool? a, bool? b) + { + Console.WriteLine(a == b); + Console.WriteLine(a != b); + + Console.WriteLine(!(a == b)); + Console.WriteLine(!(a != b)); + + Console.WriteLine(a & b); + Console.WriteLine(a | b); + Console.WriteLine(a ^ b); + Console.WriteLine(a ?? b); + Console.WriteLine(!a); + a &= b; + a |= b; + a ^= b; + } + + public static void BoolValueComplex(bool? a, Func x) + { + Console.WriteLine(a == x()); + Console.WriteLine(a != x()); + + Console.WriteLine(x() == a); + Console.WriteLine(x() != a); + + Console.WriteLine(!(a == x())); + Console.WriteLine(!(a != x())); + + Console.WriteLine(a & x()); + Console.WriteLine(a | x()); + Console.WriteLine(a ^ x()); + Console.WriteLine(a ?? x()); + a &= x(); + a |= x(); + a ^= x(); + + Console.WriteLine(x() ^ a); + (new bool?[0])[0] ^= x(); + } + + public static void BoolValueConst(bool? a) + { + Console.WriteLine(a == true); + Console.WriteLine(a != true); + Console.WriteLine(a == false); + Console.WriteLine(a != false); + Console.WriteLine(a ?? true); + Console.WriteLine(a ?? false); + } + + public static void IntBasic(int? a, int? b) + { + if (a == b) + { + Console.WriteLine(); + } + if (a != b) + { + Console.WriteLine(); + } + if (a > b) + { + Console.WriteLine(); + } + if (a < b) + { + Console.WriteLine(); + } + if (a >= b) + { + Console.WriteLine(); + } + if (a <= b) + { + Console.WriteLine(); + } + + if (!(a == b)) + { + Console.WriteLine(); + } + if (!(a != b)) + { + Console.WriteLine(); + } + if (!(a > b)) + { + Console.WriteLine(); + } + } + + public static void IntComplex(int? a, Func x) + { + if (a == x()) + { + Console.WriteLine(); + } + if (a != x()) + { + Console.WriteLine(); + } + if (a > x()) + { + Console.WriteLine(); + } + + if (x() == a) + { + Console.WriteLine(); + } + if (x() != a) + { + Console.WriteLine(); + } + if (x() > a) + { + Console.WriteLine(); + } + + if (!(a == x())) + { + Console.WriteLine(); + } + if (!(a != x())) + { + Console.WriteLine(); + } + if (!(a > x())) + { + Console.WriteLine(); + } + } + + public static void IntConst(int? a) + { + if (a == 2) + { + Console.WriteLine(); + } + if (a != 2) + { + Console.WriteLine(); + } + if (a > 2) + { + Console.WriteLine(); + } + + if (2 == a) + { + Console.WriteLine(); + } + if (2 != a) + { + Console.WriteLine(); + } + if (2 > a) + { + Console.WriteLine(); + } + } + + public static void IntValueBasic(int? a, int? b) + { + Console.WriteLine(a == b); + Console.WriteLine(a != b); + Console.WriteLine(a > b); + + Console.WriteLine(!(a == b)); + Console.WriteLine(!(a != b)); + Console.WriteLine(!(a > b)); + + Console.WriteLine(a + b); + Console.WriteLine(a - b); + Console.WriteLine(a * b); + Console.WriteLine(a / b); + Console.WriteLine(a % b); + Console.WriteLine(a & b); + Console.WriteLine(a | b); + Console.WriteLine(a ^ b); + Console.WriteLine(a << b); + Console.WriteLine(a >> b); + Console.WriteLine(a ?? b); + Console.WriteLine(-a); + Console.WriteLine(~a); + // TODO: + //Console.WriteLine(a++); + //Console.WriteLine(a--); + Console.WriteLine(++a); + Console.WriteLine(--a); + a += b; + a -= b; + a *= b; + a /= b; + a %= b; + a &= b; + a |= b; + a ^= b; + a <<= b; + a >>= b; + } + + public static void IntValueComplex(int? a, Func x) + { + Console.WriteLine(a == x()); + Console.WriteLine(a != x()); + Console.WriteLine(a > x()); + + Console.WriteLine(x() == a); + Console.WriteLine(x() != a); + Console.WriteLine(x() > a); + + Console.WriteLine(a + x()); + Console.WriteLine(a - x()); + Console.WriteLine(a * x()); + Console.WriteLine(a / x()); + Console.WriteLine(a % x()); + Console.WriteLine(a & x()); + Console.WriteLine(a | x()); + Console.WriteLine(a ^ x()); + Console.WriteLine(a << x()); + Console.WriteLine(a >> x()); + Console.WriteLine(a ?? x()); + a += x(); + a -= x(); + a *= x(); + a /= x(); + a %= x(); + a &= x(); + a |= x(); + a ^= x(); + a <<= x(); + a >>= x(); + + Console.WriteLine(x() + a); + (new int?[0])[0] += x(); + } + + public static void IntValueConst(int? a) + { + Console.WriteLine(a == 2); + Console.WriteLine(a != 2); + Console.WriteLine(a > 2); + + Console.WriteLine(2 == a); + Console.WriteLine(2 != a); + Console.WriteLine(2 > a); + + Console.WriteLine(a + 2); + Console.WriteLine(a - 2); + Console.WriteLine(a * 2); + Console.WriteLine(a / 2); + Console.WriteLine(a % 2); + Console.WriteLine(a & 2); + Console.WriteLine(a | 2); + Console.WriteLine(a ^ 2); + Console.WriteLine(a << 2); + Console.WriteLine(a >> 2); + Console.WriteLine(a ?? 2); + a += 2; + a -= 2; + a *= 2; + a /= 2; + a %= 2; + a &= 2; + a |= 2; + a ^= 2; + a <<= 2; + a >>= 2; + + Console.WriteLine(2 + a); + } + + public static void NumberBasic(decimal? a, decimal? b) + { + if (a == b) + { + Console.WriteLine(); + } + if (a != b) + { + Console.WriteLine(); + } + if (a > b) + { + Console.WriteLine(); + } + if (a < b) + { + Console.WriteLine(); + } + if (a >= b) + { + Console.WriteLine(); + } + if (a <= b) + { + Console.WriteLine(); + } + + if (!(a == b)) + { + Console.WriteLine(); + } + if (!(a != b)) + { + Console.WriteLine(); + } + if (!(a > b)) + { + Console.WriteLine(); + } + } + + public static void NumberComplex(decimal? a, Func x) + { + if (a == x()) + { + Console.WriteLine(); + } + if (a != x()) + { + Console.WriteLine(); + } + if (a > x()) + { + Console.WriteLine(); + } + + if (x() == a) + { + Console.WriteLine(); + } + if (x() != a) + { + Console.WriteLine(); + } + if (x() > a) + { + Console.WriteLine(); + } + } + + public static void NumberConst(decimal? a) + { + if (a == 2m) + { + Console.WriteLine(); + } + if (a != 2m) + { + Console.WriteLine(); + } + if (a > 2m) + { + Console.WriteLine(); + } + + if (2m == a) + { + Console.WriteLine(); + } + if (2m != a) + { + Console.WriteLine(); + } + if (2m > a) + { + Console.WriteLine(); + } + } + + public static void NumberValueBasic(decimal? a, decimal? b) + { + Console.WriteLine(a == b); + Console.WriteLine(a != b); + Console.WriteLine(a > b); + + Console.WriteLine(!(a == b)); + Console.WriteLine(!(a != b)); + Console.WriteLine(!(a > b)); + + Console.WriteLine(a + b); + Console.WriteLine(a - b); + Console.WriteLine(a * b); + Console.WriteLine(a / b); + Console.WriteLine(a % b); + Console.WriteLine(a ?? b); + Console.WriteLine(-a); + // TODO: + //Console.WriteLine(a++); + //Console.WriteLine(a--); + //Console.WriteLine(++a); + //Console.WriteLine(--a); + a += b; + a -= b; + a *= b; + a /= b; + a %= b; + } + + public static void NumberValueComplex(decimal? a, Func x) + { + Console.WriteLine(a == x()); + Console.WriteLine(a != x()); + Console.WriteLine(a > x()); + + Console.WriteLine(x() == a); + Console.WriteLine(x() != a); + Console.WriteLine(x() > a); + + Console.WriteLine(a + x()); + Console.WriteLine(a - x()); + Console.WriteLine(a * x()); + Console.WriteLine(a / x()); + Console.WriteLine(a % x()); + Console.WriteLine(a ?? x()); + a += x(); + a -= x(); + a *= x(); + a /= x(); + a %= x(); + + Console.WriteLine(x() + a); + (new decimal?[0])[0] += x(); + } + + public static void NumberValueConst(decimal? a) + { + Console.WriteLine(a == 2m); + Console.WriteLine(a != 2m); + Console.WriteLine(a > 2m); + + Console.WriteLine(2m == a); + Console.WriteLine(2m != a); + Console.WriteLine(2m > a); + + Console.WriteLine(a + 2m); + Console.WriteLine(a - 2m); + Console.WriteLine(a * 2m); + Console.WriteLine(a / 2m); + Console.WriteLine(a % 2m); + Console.WriteLine(a ?? 2m); + a += 2m; + a -= 2m; + a *= 2m; + a /= 2m; + a %= 2m; + + Console.WriteLine(2m + a); + } + + public static void StructBasic(TS? a, TS? b) + { + if (a == b) + { + Console.WriteLine(); + } + if (a != b) + { + Console.WriteLine(); + } + if (a > b) + { + Console.WriteLine(); + } + if (a < b) + { + Console.WriteLine(); + } + if (a >= b) + { + Console.WriteLine(); + } + if (a <= b) + { + Console.WriteLine(); + } + + if (!(a == b)) + { + Console.WriteLine(); + } + if (!(a != b)) + { + Console.WriteLine(); + } + if (!(a > b)) + { + Console.WriteLine(); + } + } + + public static void StructComplex(TS? a, Func x) + { + if (a == x()) + { + Console.WriteLine(); + } + if (a != x()) + { + Console.WriteLine(); + } + if (a > x()) + { + Console.WriteLine(); + } + + if (x() == a) + { + Console.WriteLine(); + } + if (x() != a) + { + Console.WriteLine(); + } + if (x() > a) + { + Console.WriteLine(); + } + } + + public static void StructValueBasic(TS? a, TS? b, int? i) + { + Console.WriteLine(a == b); + Console.WriteLine(a != b); + Console.WriteLine(a > b); + + Console.WriteLine(!(a == b)); + Console.WriteLine(!(a != b)); + Console.WriteLine(!(a > b)); + + Console.WriteLine(a + b); + Console.WriteLine(a - b); + Console.WriteLine(a * b); + Console.WriteLine(a / b); + Console.WriteLine(a % b); + Console.WriteLine(a & b); + Console.WriteLine(a | b); + Console.WriteLine(a ^ b); + Console.WriteLine(a << i); + Console.WriteLine(a >> i); + Console.WriteLine(a ?? b); + Console.WriteLine(+a); + Console.WriteLine(-a); + Console.WriteLine(!a); + Console.WriteLine(~a); + // TODO: + //Console.WriteLine(a++); + //Console.WriteLine(a--); + //Console.WriteLine(++a); + //Console.WriteLine(--a); + //Console.WriteLine((int?)a); + a += b; + a -= b; + a *= b; + a /= b; + a %= b; + a &= b; + a |= b; + a ^= b; + a <<= i; + a >>= i; + } + + public static void StructValueComplex(TS? a, Func x, Func i) + { + Console.WriteLine(a == x()); + Console.WriteLine(a != x()); + Console.WriteLine(a > x()); + + Console.WriteLine(x() == a); + Console.WriteLine(x() != a); + Console.WriteLine(x() > a); + + Console.WriteLine(a + x()); + Console.WriteLine(a - x()); + Console.WriteLine(a * x()); + Console.WriteLine(a / x()); + Console.WriteLine(a % x()); + Console.WriteLine(a & x()); + Console.WriteLine(a | x()); + Console.WriteLine(a ^ x()); + Console.WriteLine(a << i()); + Console.WriteLine(a >> i()); + Console.WriteLine(a ?? x()); + a += x(); + a -= x(); + a *= x(); + a /= x(); + a %= x(); + a &= x(); + a |= x(); + a ^= x(); + a <<= i(); + a >>= i(); + + Console.WriteLine(x() + a); + (new TS?[0])[0] += x(); + } +} + +// dummy structure for testing custom operators +[StructLayout(LayoutKind.Sequential, Size = 1)] +public struct TS +{ + // unary + public static TS operator +(TS a) + { + throw null; + } + public static TS operator -(TS a) + { + throw null; + } + public static TS operator !(TS a) + { + throw null; + } + public static TS operator ~(TS a) + { + throw null; + } + public static TS operator ++(TS a) + { + throw null; + } + public static TS operator --(TS a) + { + throw null; + } + + public static explicit operator int(TS a) + { + throw null; + } + + // binary + public static TS operator +(TS a, TS b) + { + throw null; + } + public static TS operator -(TS a, TS b) + { + throw null; + } + public static TS operator *(TS a, TS b) + { + throw null; + } + public static TS operator /(TS a, TS b) + { + throw null; + } + public static TS operator %(TS a, TS b) + { + throw null; + } + public static TS operator &(TS a, TS b) + { + throw null; + } + public static TS operator |(TS a, TS b) + { + throw null; + } + public static TS operator ^(TS a, TS b) + { + throw null; + } + public static TS operator <<(TS a, int b) + { + throw null; + } + public static TS operator >>(TS a, int b) + { + throw null; + } + + // comparisons + public static bool operator ==(TS a, TS b) + { + throw null; + } + public static bool operator !=(TS a, TS b) + { + throw null; + } + public static bool operator <(TS a, TS b) + { + throw null; + } + public static bool operator <=(TS a, TS b) + { + throw null; + } + public static bool operator >(TS a, TS b) + { + throw null; + } + public static bool operator >=(TS a, TS b) + { + throw null; + } + + public override bool Equals(object obj) + { + throw null; + } + public override int GetHashCode() + { + throw null; + } +} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Lock.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Lock.cs new file mode 100644 index 0000000000..da5a59c74f --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/Lock.cs @@ -0,0 +1,38 @@ +// Copyright (c) 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; + +public class Lock +{ + public void LockThis() + { + lock (this) + { + Console.WriteLine(); + } + } + + public void LockOnType() + { + lock (typeof(Lock)) + { + Console.WriteLine(); + } + } +} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/PInvoke.cs b/main/contrib/ICSharpCode.Decompiler/Tests/PInvoke.cs new file mode 100644 index 0000000000..0c828ac696 --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/PInvoke.cs @@ -0,0 +1,96 @@ +// 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.InteropServices; + +// P/Invoke and marshalling attribute tests +public class PInvoke +{ + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 2)] + public struct MarshalAsTest + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public uint[] FixedArray; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.Bool)] + public int[] FixedBoolArray; + + [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] + public string[] SafeBStrArray; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] + public string FixedString; + } + + [StructLayout(LayoutKind.Explicit)] + public struct Rect + { + [FieldOffset(0)] + public int left; + [FieldOffset(4)] + public int top; + [FieldOffset(8)] + public int right; + [FieldOffset(12)] + public int bottom; + } + + public static decimal MarshalAttributesOnPropertyAccessors + { + [return: MarshalAs(UnmanagedType.Currency)] + get + { + return 0m; + } + [param: MarshalAs(UnmanagedType.Currency)] + set + { + } + } + + [DllImport("xyz.dll", CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool Method([MarshalAs(UnmanagedType.LPStr)] string input); + + [DllImport("xyz.dll")] + private static extern void New1(int ElemCnt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] ar); + + [DllImport("xyz.dll")] + private static extern void New2([MarshalAs(UnmanagedType.LPArray, SizeConst = 128)] int[] ar); + + [DllImport("xyz.dll")] + private static extern void New3([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Bool, SizeConst = 64, SizeParamIndex = 1)] int[] ar); + + public void CustomMarshal1([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "MyCompany.MyMarshaler")] object o) + { + } + + public void CustomMarshal2([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "MyCompany.MyMarshaler", MarshalCookie = "Cookie")] object o) + { + } + + [DllImport("ws2_32.dll", SetLastError = true)] + internal static extern IntPtr ioctlsocket([In] IntPtr socketHandle, [In] int cmd, [In] [Out] ref int argp); + + public void CallMethodWithInOutParameter() + { + int num = 0; + PInvoke.ioctlsocket(IntPtr.Zero, 0, ref num); + } +} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/QueryExpressions.cs b/main/contrib/ICSharpCode.Decompiler/Tests/QueryExpressions.cs new file mode 100644 index 0000000000..d8b6e06234 --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/QueryExpressions.cs @@ -0,0 +1,188 @@ +// Copyright (c) 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; + +public class QueryExpressions +{ + public class Customer + { + public int CustomerID; + public IEnumerable Orders; + public string Name; + public string Country; + public string City; + } + + public class Order + { + public int OrderID; + public DateTime OrderDate; + public QueryExpressions.Customer Customer; + public int CustomerID; + public decimal Total; + public IEnumerable Details; + } + + public class OrderDetail + { + public decimal UnitPrice; + public int Quantity; + } + + public IEnumerable customers; + public IEnumerable orders; + + public object MultipleWhere() + { + return from c in this.customers + where c.Orders.Count() > 10 + where c.Country == "DE" + select c; + } + + public object SelectManyFollowedBySelect() + { + return from c in this.customers + from o in c.Orders + select new + { + c.Name, + o.OrderID, + o.Total + }; + } + + public object SelectManyFollowedByOrderBy() + { + return from c in this.customers + from o in c.Orders + orderby o.Total descending + select new + { + c.Name, + o.OrderID, + o.Total + }; + } + + public object MultipleSelectManyFollowedBySelect() + { + return from c in this.customers + from o in c.Orders + from d in o.Details + select new + { + c.Name, + o.OrderID, + d.Quantity + }; + } + + public object MultipleSelectManyFollowedByLet() + { + return from c in this.customers + from o in c.Orders + from d in o.Details + let x = d.Quantity * d.UnitPrice + select new + { + c.Name, + o.OrderID, + x + }; + } + + public object FromLetWhereSelect() + { + return from o in this.orders + let t = o.Details.Sum((QueryExpressions.OrderDetail d) => d.UnitPrice * d.Quantity) + where t >= 1000m + select new + { + OrderID = o.OrderID, + Total = t + }; + } + + public object MultipleLet() + { + return from a in this.customers + let b = a.Country + let c = a.Name + select b + c; + } + + public object Join() + { + return from c in this.customers + join o in this.orders on c.CustomerID equals o.CustomerID + select new + { + c.Name, + o.OrderDate, + o.Total + }; + } + + public object JoinInto() + { + return from c in this.customers + join o in this.orders on c.CustomerID equals o.CustomerID into co + let n = co.Count() + where n >= 10 + select new + { + Name = c.Name, + OrderCount = n + }; + } + + public object OrderBy() + { + return from o in this.orders + orderby o.Customer.Name, o.Total descending + select o; + } + + public object GroupBy() + { + return from c in this.customers + group c.Name by c.Country; + } + + public object ExplicitType() + { + return from QueryExpressions.Customer c in this.customers + where c.City == "London" + select c; + } + + public object QueryContinuation() + { + return from c in this.customers + group c by c.Country into g + select new + { + Country = g.Key, + CustCount = g.Count() + }; + } +} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs b/main/contrib/ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs new file mode 100644 index 0000000000..760bb862d3 --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs @@ -0,0 +1,153 @@ +// Copyright (c) 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; + +public class TypeAnalysisTests +{ + public byte SubtractFrom256(byte b) + { + return (byte)(256 - (int)b); + } + + #region Shift + public int LShiftInteger(int num1, int num2) + { + return num1 << num2; + } + + public uint LShiftUnsignedInteger(uint num1, uint num2) + { + return num1 << (int)num2; + } + + public long LShiftLong(long num1, long num2) + { + return num1 << (int)num2; + } + + public ulong LShiftUnsignedLong(ulong num1, ulong num2) + { + return num1 << (int)num2; + } + + public int RShiftInteger(int num1, int num2) + { + return num1 >> num2; + } + + public uint RShiftUnsignedInteger(uint num1, int num2) + { + return num1 >> num2; + } + + public long RShiftLong(long num1, long num2) + { + return num1 >> (int)num2; + } + + public ulong RShiftUnsignedLong(ulong num1, ulong num2) + { + return num1 >> (int)num2; + } + + public int ShiftByte(byte num) + { + return (int)num << 8; + } + + public int RShiftByte(byte num) + { + return num >> 8; + } + + public uint RShiftByteWithZeroExtension(byte num) + { + return (uint)num >> 8; + } + + public int RShiftByteAsSByte(byte num) + { + return (sbyte)num >> 8; + } + + public int RShiftSByte(sbyte num) + { + return num >> 8; + } + + public uint RShiftSByteWithZeroExtension(sbyte num) + { + return (uint)num >> 8; + } + + public int RShiftSByteAsByte(sbyte num) + { + return (byte)num >> 8; + } + #endregion + + public int GetHashCode(long num) + { + return (int)num ^ (int)(num >> 32); + } + + public void TernaryOp(Random a, Random b, bool c) + { + if ((c ? a : b) == null) + { + Console.WriteLine(); + } + } + + public void OperatorIs(object o) + { + Console.WriteLine(o is Random); + Console.WriteLine(!(o is Random)); + } + + public byte[] CreateArrayWithInt(int length) + { + return new byte[length]; + } + + public byte[] CreateArrayWithLong(long length) + { + return new byte[length]; + } + + public byte[] CreateArrayWithUInt(uint length) + { + return new byte[length]; + } + + public byte[] CreateArrayWithULong(ulong length) + { + return new byte[length]; + } + + public StringComparison EnumDiffNumber(StringComparison data) + { + return data - 1; + } + + public int EnumDiff(StringComparison a, StringComparison b) + { + return Math.Abs(a - b); + } +} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs new file mode 100644 index 0000000000..99bf4279e3 --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs @@ -0,0 +1,17 @@ +using System; + +namespace ClassMultiInterface +{ + public interface IA + { + } + public interface IA2 : IA + { + } + public interface IB + { + } + public class C : IA2, IB + { + } +} diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs b/main/contrib/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs new file mode 100644 index 0000000000..80f5b0371e --- /dev/null +++ b/main/contrib/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs @@ -0,0 +1,41 @@ +// Copyright (c) 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; + +public class UndocumentedExpressions +{ + public static int GetArgCount(__arglist) + { + ArgIterator argIterator = new ArgIterator(__arglist); + return argIterator.GetRemainingCount(); + } + + public static void MakeTypedRef(object o) + { + TypedReference tr = __makeref(o); + UndocumentedExpressions.AcceptTypedRef(tr); + } + + private static void AcceptTypedRef(TypedReference tr) + { + Console.WriteLine("Value is: " + __refvalue(tr, object).ToString()); + Console.WriteLine("Type is: " + __reftype(tr).Name); + __refvalue(tr, object) = 1; + } +} -- cgit v1.2.3 From f0d130736e013ab2c5a88898dfcc0de124626937 Mon Sep 17 00:00:00 2001 From: Matt Ward Date: Fri, 14 Aug 2015 10:10:30 +0100 Subject: [Decompiler] Fix build on Windows. --- main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs | 1 - main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 1 - 2 files changed, 2 deletions(-) (limited to 'main/contrib') diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index ce62816d7a..ecf1e9fc5f 100644 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -24,7 +24,6 @@ using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.NRefactory.Utils; using Mono.Cecil; using Mono.Cecil.Cil; -using Mono.CSharp; namespace ICSharpCode.Decompiler.ILAst { diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 9cd42a14fc..179b40a673 100644 --- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -27,7 +27,6 @@ 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 -- cgit v1.2.3