From 897ac7fe0441103f780875bcacf52434ef723d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Mon, 7 Mar 2011 00:12:40 +0000 Subject: [PATCH 1/7] Moved inlineing from Builder to Optimizer --- ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs | 59 +------------ .../ILAst/ILAstOptimizer.cs | 88 +++++++++++++++++++ 2 files changed, 90 insertions(+), 57 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index 8a060384c..4ee3a4546 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -201,9 +201,6 @@ namespace ICSharpCode.Decompiler.ILAst MethodDefinition methodDef; bool optimize; - Dictionary instrToByteCode = new Dictionary(); - Dictionary allowInline = new Dictionary(); - // Virtual instructions to load exception on stack Dictionary ldexceptions = new Dictionary(); @@ -223,6 +220,8 @@ namespace ICSharpCode.Decompiler.ILAst List StackAnalysis(MethodDefinition methodDef) { + Dictionary instrToByteCode = new Dictionary(); + // Create temporary structure for the stack analysis List body = new List(methodDef.Body.Instructions.Count); List prefixes = null; @@ -406,9 +405,6 @@ namespace ICSharpCode.Decompiler.ILAst } pushedBy.StoreTo.Add(tmpVar); } - if (byteCode.StackBefore[i].PushedBy.Length == 1) { - allowInline[tmpVar] = true; - } argIdx++; } } @@ -500,13 +496,6 @@ namespace ICSharpCode.Decompiler.ILAst newVars.Add(mergedVar); } } - - // Permit inlining - foreach(VariableInfo newVar in newVars) { - if (newVar.Stores.Count == 1 && newVar.Loads.Count == 1) { - allowInline[newVar.Variable] = true; - } - } } // Set bytecode operands @@ -667,50 +656,6 @@ namespace ICSharpCode.Decompiler.ILAst } } - // Try to in-line stloc / ldloc pairs - for(int i = 0; i < ast.Count - 1; i++) { - if (i < 0) continue; - - ILExpression currExpr = ast[i] as ILExpression; - ILExpression nextExpr = ast[i + 1] as ILExpression; - - if (currExpr != null && nextExpr != null && currExpr.Code == ILCode.Stloc) { - - // If the next expression is generated stloc, look inside - if (nextExpr.Code == ILCode.Stloc && ((ILVariable)nextExpr.Operand).IsGenerated) { - nextExpr = nextExpr.Arguments[0]; - } - - // Find the use of the 'expr' - for(int j = 0; j < nextExpr.Arguments.Count; j++) { - ILExpression arg = nextExpr.Arguments[j]; - - // We are moving the expression evaluation past the other aguments. - // It is ok to pass ldloc because the expression can not contain stloc and thus the ldcoc will still return the same value - // Do not inline ldloca - if (arg.Code == ILCode.Ldloc) { - if (arg.Operand == currExpr.Operand) { - bool canInline; - allowInline.TryGetValue((ILVariable)arg.Operand, out canInline); - - if (canInline) { - // Assigne the ranges for optimized away instrustions somewhere - currExpr.Arguments[0].ILRanges.AddRange(currExpr.ILRanges); - currExpr.Arguments[0].ILRanges.AddRange(nextExpr.Arguments[j].ILRanges); - - ast.RemoveAt(i); - nextExpr.Arguments[j] = currExpr.Arguments[0]; // Inline the stloc body - i -= 2; // Try the same index again - break; // Found - } - } - } else { - break; // Side-effects - } - } - } - } - return ast; } } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index fa0a6b15f..023010baa 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -11,6 +11,7 @@ namespace ICSharpCode.Decompiler.ILAst { public enum ILAstOptimizationStep { + InlineVariables, ReduceBranchInstructionSet, YieldReturn, SplitToMovableBlocks, @@ -34,6 +35,9 @@ namespace ICSharpCode.Decompiler.ILAst public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) { + if (abortBeforeStep == ILAstOptimizationStep.InlineVariables) return; + InlineVariables(method); + if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { ReduceBranchInstructionSet(block); @@ -95,6 +99,83 @@ namespace ICSharpCode.Decompiler.ILAst GotoRemoval.RemoveRedundantCode(method); } + void InlineVariables(ILBlock method) + { + // Analyse the whole method + Dictionary numStloc = new Dictionary(); + Dictionary numLdloc = new Dictionary(); + Dictionary numLdloca = new Dictionary(); + + foreach(ILExpression expr in method.GetSelfAndChildrenRecursive()) { + ILVariable locVar = expr.Operand as ILVariable; + if (locVar != null) { + if (expr.Code == ILCode.Stloc) { + numStloc[locVar] = numStloc.GetOrDefault(locVar) + 1; + } else if (expr.Code == ILCode.Ldloc) { + numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + 1; + } else if (expr.Code == ILCode.Ldloca) { + numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + 1; + } else { + throw new NotSupportedException(expr.Code.ToString()); + } + } + } + + // Inline all blocks + foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { + List body = block.Body; + for(int i = 0; i < body.Count - 1;) { + ILExpression nextExpr = body[i + 1] as ILExpression; + ILVariable locVar; + ILExpression expr; + ILExpression ldParent; + int ldPos; + if (body[i].Match(ILCode.Stloc, out locVar, out expr) && + numStloc.GetOrDefault(locVar) == 1 && + numLdloc.GetOrDefault(locVar) == 1 && + numLdloca.GetOrDefault(locVar) == 0 && + nextExpr != null && + FindLdloc(nextExpr, locVar, out ldParent, out ldPos) == true && + ldParent != null) + { + // Assign the ranges of the optimized instrustions + expr.ILRanges.AddRange(((ILExpression)body[i]).ILRanges); + expr.ILRanges.AddRange(ldParent.Arguments[ldPos].ILRanges); + + // We are moving the expression evaluation past the other aguments. + // It is ok to pass ldloc because the expression can not contain stloc and thus the ldloc will still return the same value + body.RemoveAt(i); + ldParent.Arguments[ldPos] = expr; // Inline the stloc body + i = Math.Max(0, i - 1); // Go back one step + } else { + i++; + } + } + } + } + + /// + /// Finds the position to inline to. + /// + /// true = found; false = cannot continue search; null = not found + static bool? FindLdloc(ILExpression expr, ILVariable v, out ILExpression parent, out int pos) + { + parent = null; + pos = 0; + for (int i = 0; i < expr.Arguments.Count; i++) { + ILExpression arg = expr.Arguments[i]; + if (arg.Code == ILCode.Ldloc && arg.Operand == v) { + parent = expr; + pos = i; + return true; + } + bool? r = FindLdloc(arg, v, out parent, out pos); + if (r != null) + return r; + } + return expr.Code == ILCode.Ldloc ? (bool?)null : false; + } + /// /// Reduces the branch codes to just br and brtrue. /// Moves ILRanges to the branch argument @@ -941,6 +1022,13 @@ namespace ICSharpCode.Decompiler.ILAst return true; } + public static V GetOrDefault(this Dictionary dict, K key) + { + V ret; + dict.TryGetValue(key, out ret); + return ret; + } + public static void RemoveOrThrow(this ICollection collection, T item) { if (!collection.Remove(item)) From aaad162de6790c8b314a6b274b0a3b3ee755c7c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Mon, 7 Mar 2011 00:20:00 +0000 Subject: [PATCH 2/7] Storing temporary variables in reverse produces better code --- ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index 4ee3a4546..f035f521a 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -650,7 +650,7 @@ namespace ICSharpCode.Decompiler.ILAst } else { ILVariable tmpVar = new ILVariable() { Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true }; ast.Add(new ILExpression(ILCode.Stloc, tmpVar, expr)); - foreach(ILVariable storeTo in byteCode.StoreTo) { + foreach(ILVariable storeTo in byteCode.StoreTo.AsEnumerable().Reverse()) { ast.Add(new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, tmpVar))); } } From efd04b31d8f85902c18772fa2d967a8669f9bb8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Mon, 7 Mar 2011 00:50:21 +0000 Subject: [PATCH 3/7] Simple goto and nop removal --- .../ILAst/ILAstOptimizer.cs | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 023010baa..36ac4cdda 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -11,6 +11,7 @@ namespace ICSharpCode.Decompiler.ILAst { public enum ILAstOptimizationStep { + SimpleGotoAndNopRemoval, InlineVariables, ReduceBranchInstructionSet, YieldReturn, @@ -35,7 +36,11 @@ namespace ICSharpCode.Decompiler.ILAst public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) { + if (abortBeforeStep == ILAstOptimizationStep.SimpleGotoAndNopRemoval) return; + SimpleGotoAndNopRemoval(method); + if (abortBeforeStep == ILAstOptimizationStep.InlineVariables) return; + // Works better are simple goto removal because of the following pattern: stloc X; br Next; Next:; ldloc X InlineVariables(method); if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return; @@ -82,6 +87,7 @@ namespace ICSharpCode.Decompiler.ILAst FlattenBasicBlocks(method); if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return; + SimpleGotoAndNopRemoval(method); new GotoRemoval().RemoveGotos(method); if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return; @@ -99,6 +105,32 @@ namespace ICSharpCode.Decompiler.ILAst GotoRemoval.RemoveRedundantCode(method); } + void SimpleGotoAndNopRemoval(ILBlock method) + { + Dictionary labelRefCount = new Dictionary(); + foreach (ILLabel target in method.GetSelfAndChildrenRecursive().SelectMany(e => e.GetBranchTargets())) { + labelRefCount[target] = labelRefCount.GetOrDefault(target) + 1; + } + + foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { + List body = block.Body; + List newBody = new List(body.Count); + for (int i = 0; i < body.Count; i++) { + ILLabel target; + if (body[i].Match(ILCode.Br, out target) && i+1 < body.Count && body[i+1] == target) { + // Ignore the branch TODO: ILRanges + if (labelRefCount[target] == 1) + i++; // Ignore the label as well + } else if (body[i].Match(ILCode.Nop)){ + // Ignore nop TODO: ILRanges + } else { + newBody.Add(body[i]); + } + } + block.Body = newBody; + } + } + void InlineVariables(ILBlock method) { // Analyse the whole method @@ -217,10 +249,6 @@ namespace ICSharpCode.Decompiler.ILAst /// void SplitToBasicBlocks(ILBlock block) { - // Remve no-ops - // TODO: Assign the no-op range to someting - block.Body = block.Body.Where(n => !(n is ILExpression && ((ILExpression)n).Code == ILCode.Nop)).ToList(); - List basicBlocks = new List(); ILBasicBlock basicBlock = new ILBasicBlock() { From 5a2a1119a2631767ecc82db568711d76e23e34cf Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 6 Mar 2011 22:19:32 +0100 Subject: [PATCH 4/7] Decompile try-fault blocks into try-catch-rethrow. --- ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 4566192cb..ee184461a 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -147,6 +147,12 @@ namespace ICSharpCode.Decompiler.Ast } if (tryCatchNode.FinallyBlock != null) tryCatchStmt.FinallyBlock = TransformBlock(tryCatchNode.FinallyBlock); + if (tryCatchNode.FaultBlock != null) { + CatchClause cc = new CatchClause(); + cc.Body = TransformBlock(tryCatchNode.FaultBlock); + cc.Body.Add(new ThrowStatement()); // rethrow + tryCatchStmt.CatchClauses.Add(cc); + } yield return tryCatchStmt; } else if (node is ILBlock) { yield return TransformBlock((ILBlock)node); From 795108944712a639dba9aa80d8a48e740513eb49 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 7 Mar 2011 02:01:08 +0100 Subject: [PATCH 5/7] Remove code made redundant by SimpleGotoAndNopRemoval. --- .../ILAst/YieldReturnDecompiler.cs | 31 +++---------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs index 28da4308e..9e653d374 100644 --- a/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs @@ -118,8 +118,6 @@ namespace ICSharpCode.Decompiler.ILAst // the compiler might skip the above instruction in release builds; in that case, it directly returns stloc.Operand var2 = var1; } - if (!SkipDummyBr(method, ref i)) - return false; ILExpression retArg; if (i < method.Body.Count && method.Body[i].Match(ILCode.Ret, out retArg)) { // ret(ldloc(var_2)) @@ -146,17 +144,6 @@ namespace ICSharpCode.Decompiler.ILAst return method as MethodDefinition; } - bool SkipDummyBr(ILBlock method, ref int i) - { - ILLabel target; - if (i + 1 < method.Body.Count && method.Body[i].Match(ILCode.Br, out target)) { - if (target != method.Body[i + 1]) - return false; - i += 2; - } - return true; - } - bool MatchEnumeratorCreationNewObj(ILExpression expr, out MethodDefinition ctor) { // newobj(CurrentType/...::.ctor, ldc.i4(-2)) @@ -243,7 +230,7 @@ namespace ICSharpCode.Decompiler.ILAst StoreToVariable v = new StoreToVariable(new ILExpression(ILCode.Ldfld, ILExpression.AnyOperand, LoadFromArgument.This)); if (v.Match(method.Body[0])) { int i = 1; - if (SkipDummyBr(method, ref i) && i == method.Body.Count - 1) { + if (i == method.Body.Count - 1) { if (new ILExpression(ILCode.Ret, null, new LoadFromVariable(v)).Match(method.Body[i])) { currentField = GetFieldDefinition(((ILExpression)method.Body[0]).Arguments[0].Operand as FieldReference); } @@ -310,16 +297,14 @@ namespace ICSharpCode.Decompiler.ILAst foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive()) { Interval interval = ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval(); var finallyBody = tryFinally.FinallyBlock.Body; - if (!(finallyBody.Count == 2 || finallyBody.Count == 3)) + if (finallyBody.Count != 2) throw new YieldAnalysisFailedException(); ILExpression call = finallyBody[0] as ILExpression; if (call == null || call.Code != ILCode.Call || call.Arguments.Count != 1) throw new YieldAnalysisFailedException(); if (call.Arguments[0].Code != ILCode.Ldarg || ((ParameterDefinition)call.Arguments[0].Operand).Index >= 0) throw new YieldAnalysisFailedException(); - if (finallyBody.Count == 3 && !finallyBody[1].Match(ILCode.Nop)) - throw new YieldAnalysisFailedException(); - if (!finallyBody[finallyBody.Count - 1].Match(ILCode.Endfinally)) + if (!finallyBody[1].Match(ILCode.Endfinally)) throw new YieldAnalysisFailedException(); MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference); @@ -651,8 +636,6 @@ namespace ICSharpCode.Decompiler.ILAst if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg)) throw new YieldAnalysisFailedException(); - ilMethod.Body.RemoveAll(n => n.Match(ILCode.Nop)); // remove nops - // There are two possibilities: if (lastReturnArg.Code == ILCode.Ldloc) { // a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks) @@ -682,7 +665,7 @@ namespace ICSharpCode.Decompiler.ILAst ILBlock faultBlock = tryFaultBlock.FaultBlock; // Ensure the fault block contains the call to Dispose(). - if (!(faultBlock.Body.Count == 2 || faultBlock.Body.Count == 3)) + if (faultBlock.Body.Count != 2) throw new YieldAnalysisFailedException(); MethodReference disposeMethodRef; ILExpression disposeArg; @@ -690,13 +673,10 @@ namespace ICSharpCode.Decompiler.ILAst throw new YieldAnalysisFailedException(); if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !LoadFromArgument.This.Match(disposeArg)) throw new YieldAnalysisFailedException(); - if (faultBlock.Body.Count == 3 && !faultBlock.Body[1].Match(ILCode.Nop)) - throw new YieldAnalysisFailedException(); - if (!faultBlock.Body[faultBlock.Body.Count - 1].Match(ILCode.Endfinally)) + if (!faultBlock.Body[1].Match(ILCode.Endfinally)) throw new YieldAnalysisFailedException(); body = tryFaultBlock.TryBlock.Body; - body.RemoveAll(n => n.Match(ILCode.Nop)); // remove nops bodyLength = body.Count; } else { // no try-finally blocks @@ -872,7 +852,6 @@ namespace ICSharpCode.Decompiler.ILAst ILBlock ConvertFinallyBlock(MethodDefinition finallyMethod) { ILBlock block = CreateILAst(finallyMethod); - block.Body.RemoveAll(n => n.Match(ILCode.Nop)); // Get rid of assignment to state FieldReference stfld; List args; From ec0916c595768b17a7135930471185cfdaa329ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Mon, 7 Mar 2011 01:20:37 +0000 Subject: [PATCH 6/7] Perform inlining at the end --- ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 36ac4cdda..708dbd123 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -24,6 +24,7 @@ namespace ICSharpCode.Decompiler.ILAst DuplicateReturns, FlattenIfStatements, PeepholeTransforms, + InlineVariables2, TypeInference, None } @@ -40,7 +41,7 @@ namespace ICSharpCode.Decompiler.ILAst SimpleGotoAndNopRemoval(method); if (abortBeforeStep == ILAstOptimizationStep.InlineVariables) return; - // Works better are simple goto removal because of the following pattern: stloc X; br Next; Next:; ldloc X + // Works better after simple goto removal because of the following debug pattern: stloc X; br Next; Next:; ldloc X InlineVariables(method); if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return; @@ -99,6 +100,9 @@ namespace ICSharpCode.Decompiler.ILAst if (abortBeforeStep == ILAstOptimizationStep.PeepholeTransforms) return; PeepholeTransforms.Run(context, method); + if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return; + InlineVariables(method); + if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; TypeAnalysis.Run(context, method); @@ -195,6 +199,11 @@ namespace ICSharpCode.Decompiler.ILAst parent = null; pos = 0; for (int i = 0; i < expr.Arguments.Count; i++) { + // Stop when seeing an opcode that does not guarantee that its operands will be evaluated + // Inlining in that case migth result in the inlined expresion not being evaluted + if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp)) + return false; + ILExpression arg = expr.Arguments[i]; if (arg.Code == ILCode.Ldloc && arg.Operand == v) { parent = expr; From 5078d2ef89ad965cdeb46c5781adbba8dfda42fe Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 7 Mar 2011 07:49:34 +0100 Subject: [PATCH 7/7] Handle calls to value type constructors. Closes #66. --- .../Ast/AstMethodBodyBuilder.cs | 10 ++++++++++ ICSharpCode.Decompiler/DecompilerSettings.cs | 2 -- ICSharpCode.Decompiler/Tests/ValueTypes.cs | 16 ++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index ee184461a..a85b773f9 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -535,6 +535,16 @@ namespace ICSharpCode.Decompiler.Ast } } + if (cecilMethod.Name == ".ctor" && cecilMethod.DeclaringType.IsValueType) { + // On value types, the constructor can be called. + // This is equivalent to 'target = new ValueType(args);'. + ObjectCreateExpression oce = new ObjectCreateExpression(); + oce.Type = AstBuilder.ConvertType(cecilMethod.DeclaringType); + AdjustArgumentsForMethodCall(cecilMethod, methodArgs); + oce.Arguments.AddRange(methodArgs); + return new AssignmentExpression(target, oce); + } + if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) { return target.Indexer(methodArgs); } else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) { diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 53736311a..a1770d771 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -41,8 +41,6 @@ namespace ICSharpCode.Decompiler } } - public event EventHandler YieldReturnChanged; - public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) diff --git a/ICSharpCode.Decompiler/Tests/ValueTypes.cs b/ICSharpCode.Decompiler/Tests/ValueTypes.cs index ba863f302..e2e2d20fc 100644 --- a/ICSharpCode.Decompiler/Tests/ValueTypes.cs +++ b/ICSharpCode.Decompiler/Tests/ValueTypes.cs @@ -9,6 +9,11 @@ public static class ValueTypes { public int Field; + public S(int field) + { + this.Field = field; + } + public void SetField() { this.Field = 5; @@ -46,6 +51,17 @@ public static class ValueTypes p = default(S); } + public static S CallValueTypeCtor1() + { + return new S(10); + } + + public static S CallValueTypeCtor2() + { + S s = new S(10); + return s; + } + public static S Copy1(S p) { return p;