diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 64aa843f03..8cfada0935 100644 --- a/src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -167,7 +167,12 @@ namespace ICSharpCode.Decompiler.Ast 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", @@ -176,22 +181,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 diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index fae41f6860..f31d1c676b 100644 --- a/src/Libraries/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/src/Libraries/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -849,7 +849,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; @@ -975,10 +975,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 +1075,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/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs index 32f7cb0dbe..ffd6f5ac76 100644 --- a/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs +++ b/src/Libraries/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/src/Libraries/ICSharpCode.Decompiler/DecompilerSettings.cs b/src/Libraries/ICSharpCode.Decompiler/DecompilerSettings.cs index 36c91745ca..6412db5f5a 100644 --- a/src/Libraries/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/src/Libraries/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -283,6 +283,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; /// diff --git a/src/Libraries/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/src/Libraries/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs index 4c2080045e..ea6e54dbea 100644 --- a/src/Libraries/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs +++ b/src/Libraries/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/src/Libraries/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/src/Libraries/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs index a45a718207..ef6d32e69d 100644 --- a/src/Libraries/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs +++ b/src/Libraries/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/src/Libraries/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs index 4535c0f82a..0c8bb8b999 100644 --- a/src/Libraries/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs +++ b/src/Libraries/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); @@ -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/src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index da8c6c6beb..e30398e042 100644 --- a/src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/src/Libraries/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/src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index bf85ca94f4..6c8ccbe74c 100644 --- a/src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -170,11 +170,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) { @@ -225,11 +229,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); + } } } diff --git a/src/Libraries/ICSharpCode.Decompiler/ILAst/ILInlining.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/ILInlining.cs index 880592b103..11140ef083 100644 --- a/src/Libraries/ICSharpCode.Decompiler/ILAst/ILInlining.cs +++ b/src/Libraries/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/src/Libraries/ICSharpCode.Decompiler/ILAst/PatternMatching.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/PatternMatching.cs index 31fcf62b15..441088b903 100644 --- a/src/Libraries/ICSharpCode.Decompiler/ILAst/PatternMatching.cs +++ b/src/Libraries/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/src/Libraries/ICSharpCode.Decompiler/ILAst/StateRange.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/StateRange.cs index 4a55e1d0b6..3a3dfc2f34 100644 --- a/src/Libraries/ICSharpCode.Decompiler/ILAst/StateRange.cs +++ b/src/Libraries/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/src/Libraries/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs index 98255f2f2d..5fb40acf92 100644 --- a/src/Libraries/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs +++ b/src/Libraries/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/src/Libraries/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index b050353ab4..243897e36a 100644 --- a/src/Libraries/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/src/Libraries/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -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; diff --git a/src/Libraries/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs index b4b6183bf8..2da165f610 100644 --- a/src/Libraries/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs +++ b/src/Libraries/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/src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs b/src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs index f935467a23..caa2434d4e 100644 --- a/src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs +++ b/src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs @@ -1,31 +1,17 @@ -// 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. +#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 AlphaSierraPapa for the SharpDevelop Team")] +[assembly: AssemblyCopyright("Copyright 2011-2014 AlphaSierraPapa for the SharpDevelop Team")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -33,8 +19,8 @@ using System.Runtime.InteropServices; // If you need to expose a type to COM, use [ComVisible(true)] on that type. [assembly: ComVisible(false)] -[assembly: AssemblyVersion("2.1.0.1619")] -[assembly: AssemblyInformationalVersion("2.1.0.1619-214c1f73")] +[assembly: AssemblyVersion("2.2.0.1706")] +[assembly: AssemblyInformationalVersion("2.2.0.1706-cc270c8f")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/Async.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/Async.cs index ccd1222556..7629dd5e62 100644 --- a/src/Libraries/ICSharpCode.Decompiler/Tests/Async.cs +++ b/src/Libraries/ICSharpCode.Decompiler/Tests/Async.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Runtime.CompilerServices; using System.Threading.Tasks; public class Async @@ -36,6 +37,16 @@ public class Async 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"); diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs index 5d5071f676..70c00fc655 100644 --- a/src/Libraries/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs +++ b/src/Libraries/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/src/Libraries/ICSharpCode.Decompiler/Tests/DoubleConstants.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/DoubleConstants.cs new file mode 100644 index 0000000000..2bc20a7730 --- /dev/null +++ b/src/Libraries/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/src/Libraries/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/src/Libraries/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index 69598ac73f..db8b70192b 100644 --- a/src/Libraries/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/src/Libraries/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -67,6 +67,7 @@ + diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/TestRunner.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/TestRunner.cs index 009a3c6c17..14cf095930 100644 --- a/src/Libraries/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/src/Libraries/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -88,6 +88,12 @@ namespace ICSharpCode.Decompiler.Tests TestFile(@"..\..\Tests\ControlFlow.cs", optimize: false, useDebug: true); } + [Test] + public void DoubleConstants() + { + TestFile(@"..\..\Tests\DoubleConstants.cs"); + } + [Test] public void IncrementDecrement() {