diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index ef5480f17..880297416 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -170,9 +170,6 @@ namespace ICSharpCode.Decompiler.Tests [Test] public void CheckedUnchecked([ValueSource("defaultOptions")] CompilerOptions cscOptions) { - if (cscOptions.HasFlag(CompilerOptions.UseRoslyn) && cscOptions.HasFlag(CompilerOptions.Optimize)) { - Assert.Ignore("Roslyn opt replaces locals with stack slots, resulting in S_* variable names."); - } Run(cscOptions: cscOptions); } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 3ad5d70ed..1503561f0 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -110,15 +110,18 @@ namespace ICSharpCode.Decompiler.CSharp new TransformAssignment(), new NullableLiftingBlockTransform(), new CopyPropagation(), - new LoopingBlockTransform( - // per-block transforms that depend on each other, and thus need to loop (fixpoint iteration). + new StatementTransform( + // per-block transforms that depend on each other, and thus need to + // run interleaved (statement by statement). // Pretty much all transforms that open up new expression inlining // opportunities belong in this category. + new ILInlining(), + // Inlining must be first, because it doesn't trigger re-runs. + // Any other transform that opens up new inlining opportunities should call RequestRerun(). new ExpressionTransforms(), new NullCoalescingTransform(), new TransformArrayInitializers(), - new TransformCollectionAndObjectInitializers(), - new ILInlining() + new TransformCollectionAndObjectInitializers() ) } }, @@ -646,14 +649,15 @@ namespace ICSharpCode.Decompiler.CSharp int i = 0; var parameters = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index); foreach (var parameter in entityDecl.GetChildrenByRole(Roles.Parameter)) { - ILVariable v; - if (parameters.TryGetValue(i, out v)) + if (parameters.TryGetValue(i, out var v)) parameter.AddAnnotation(new ILVariableResolveResult(v, method.Parameters[i].Type)); i++; } } - var context = new ILTransformContext { Settings = settings, TypeSystem = specializingTypeSystem, CancellationToken = CancellationToken }; + var context = new ILTransformContext(function, specializingTypeSystem, settings) { + CancellationToken = CancellationToken + }; foreach (var transform in ilTransforms) { CancellationToken.ThrowIfCancellationRequested(); transform.Run(function, context); diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index d934bd0eb..c1edc9a5d 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -288,6 +288,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index 0ba14f826..b9273a821 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -377,11 +377,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow ); } var il = new ILReader(typeSystem).ReadIL(method.Body, context.CancellationToken); - il.RunTransforms(CSharpDecompiler.EarlyILTransforms(true), new ILTransformContext { - Settings = context.Settings, - CancellationToken = context.CancellationToken, - TypeSystem = context.TypeSystem - }); + il.RunTransforms(CSharpDecompiler.EarlyILTransforms(true), + new ILTransformContext(il, typeSystem, context.Settings) { + CancellationToken = context.CancellationToken + }); return il; } #endregion diff --git a/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs index 570fe988d..bae4547ed 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.IL.ControlFlow; @@ -24,11 +26,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// public class BlockTransformContext : ILTransformContext { - /// - /// The function containing the block currently being processed. - /// - public ILFunction Function { get; set; } - /// /// The block to process. /// @@ -69,6 +66,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms public IList PreOrderTransforms { get; } = new List(); public IList PostOrderTransforms { get; } = new List(); + bool running; + public override string ToString() { return $"{nameof(BlockILTransform)} ({string.Join(", ", PreOrderTransforms.Concat(PostOrderTransforms).Select(t => t.GetType().Name))})"; @@ -76,13 +75,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms public void Run(ILFunction function, ILTransformContext context) { - var blockContext = new BlockTransformContext(context); - blockContext.Function = function; - foreach (var container in function.Descendants.OfType().ToList()) { - context.CancellationToken.ThrowIfCancellationRequested(); - blockContext.ControlFlowGraph = new ControlFlowGraph(container, context.CancellationToken); - VisitBlock(blockContext.ControlFlowGraph.GetNode(container.EntryPoint), blockContext); - // TODO: handle unreachable code? + if (running) + throw new InvalidOperationException("Reentrancy detected. Transforms (and the CSharpDecompiler) are neither neither thread-safe nor re-entrant."); + try { + running = true; + var blockContext = new BlockTransformContext(context); + Debug.Assert(blockContext.Function == function); + foreach (var container in function.Descendants.OfType().ToList()) { + context.CancellationToken.ThrowIfCancellationRequested(); + blockContext.ControlFlowGraph = new ControlFlowGraph(container, context.CancellationToken); + VisitBlock(blockContext.ControlFlowGraph.GetNode(container.EntryPoint), blockContext); + // TODO: handle unreachable code? + } + } finally { + running = false; } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/CachedDelegateInitialization.cs b/ICSharpCode.Decompiler/IL/Transforms/CachedDelegateInitialization.cs index 8b281d346..6d3b833e1 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/CachedDelegateInitialization.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/CachedDelegateInitialization.cs @@ -38,7 +38,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms continue; } if (CachedDelegateInitializationWithLocal(inst)) { - ILInlining.InlineIfPossible(block, ref i, context); + ILInlining.InlineOneIfPossible(block, i, true, context); continue; } if (CachedDelegateInitializationRoslynInStaticWithLocal(inst) || CachedDelegateInitializationRoslynWithLocal(inst)) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index 10e482e7c..130349971 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -104,7 +104,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return opCode == OpCode.LdFtn || opCode == OpCode.LdVirtFtn || (allowTransformed && opCode == OpCode.ILFunction); } - internal static bool IsPotentialClosure(BlockTransformContext context, NewObj inst) + internal static bool IsPotentialClosure(ILTransformContext context, NewObj inst) { var decompilationContext = new SimpleTypeResolveContext(context.TypeSystem.Resolve(context.Function.Method)); return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, inst.Method.DeclaringTypeDefinition); @@ -150,11 +150,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter)) { v.Name = contextPrefix + v.Name; } - - function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is DelegateConstruction)), context); + + var nestedContext = new ILTransformContext(function, localTypeSystem, context.Settings) { + CancellationToken = context.CancellationToken + }; + function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is DelegateConstruction)), nestedContext); function.AcceptVisitor(new ReplaceDelegateTargetVisitor(target, function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter))); // handle nested lambdas - ((IILTransform)new DelegateConstruction()).Run(function, new ILTransformContext(context) { TypeSystem = localTypeSystem }); + ((IILTransform)new DelegateConstruction()).Run(function, nestedContext); return function; } return null; diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 31bd540dc..2c467b34d 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -29,16 +29,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Should run after inlining so that the expression patterns can be detected. /// - public class ExpressionTransforms : ILVisitor, IBlockTransform + public class ExpressionTransforms : ILVisitor, IBlockTransform, IStatementTransform { internal ILTransformContext context; - + public void Run(Block block, BlockTransformContext context) { this.context = context; Default(block); } - + + public void Run(Block block, int pos, StatementTransformContext context) + { + this.context = context; + context.StepStartGroup($"ExpressionTransforms ({block.Label}:{pos})", block.Instructions[pos]); + block.Instructions[pos].AcceptVisitor(this); + context.StepEndGroup(keepIfEmpty: true); + } + protected override void Default(ILInstruction inst) { foreach (var child in inst.Children) { @@ -310,6 +318,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms protected internal override void VisitIfInstruction(IfInstruction inst) { + inst.TrueInst.AcceptVisitor(this); + inst.FalseInst.AcceptVisitor(this); + inst = HandleConditionalOperator(inst); + // Bring LogicAnd/LogicOr into their canonical forms: // if (cond) ldc.i4 0 else RHS --> if (!cond) RHS else ldc.i4 0 // if (cond) RHS else ldc.i4 1 --> if (!cond) ldc.i4 1 else RHS @@ -324,10 +336,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms inst.FalseInst = t; inst.Condition = Comp.LogicNot(inst.Condition); } + // Process condition after our potential modifications. + inst.Condition.AcceptVisitor(this); - base.VisitIfInstruction(inst); - - inst = HandleConditionalOperator(inst); if (new NullableLiftingTransform(context).Run(inst)) return; } @@ -348,6 +359,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms var newIf = new IfInstruction(Comp.LogicNot(inst.Condition), value2, value1); newIf.ILRange = inst.ILRange; inst.ReplaceWith(new StLoc(v, newIf)); + (context as StatementTransformContext)?.RequestRerun(); // trigger potential inlining of the newly created StLoc return newIf; } return inst; diff --git a/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs index 109bae631..194fbd173 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Diagnostics; using System.Threading; using ICSharpCode.Decompiler.TypeSystem; @@ -35,18 +36,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// public class ILTransformContext { - public IDecompilerTypeSystem TypeSystem { get; set; } - public DecompilerSettings Settings { get; set; } + public ILFunction Function { get; } + public IDecompilerTypeSystem TypeSystem { get; } + public DecompilerSettings Settings { get; } public CancellationToken CancellationToken { get; set; } public Stepper Stepper { get; set; } - public ILTransformContext() + public ILTransformContext(ILFunction function, IDecompilerTypeSystem typeSystem, DecompilerSettings settings = null) { + this.Function = function ?? throw new ArgumentNullException(nameof(function)); + this.TypeSystem = typeSystem ?? throw new ArgumentNullException(nameof(typeSystem)); + this.Settings = settings ?? new DecompilerSettings(); Stepper = new Stepper(); } public ILTransformContext(ILTransformContext context) { + this.Function = context.Function; this.TypeSystem = context.TypeSystem; this.Settings = context.Settings; this.CancellationToken = context.CancellationToken; diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 2a5cf6236..65e9756c8 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2011-2015 Daniel Grunwald +// Copyright (c) 2011-2017 Daniel Grunwald // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software @@ -26,7 +26,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Performs inlining transformations. /// - public class ILInlining : IILTransform, IBlockTransform + public class ILInlining : IILTransform, IBlockTransform, IStatementTransform { public void Run(ILFunction function, ILTransformContext context) { @@ -41,6 +41,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms InlineAllInBlock(block, context); } + public void Run(Block block, int pos, StatementTransformContext context) + { + InlineOneIfPossible(block, pos, aggressive: IsCatchWhenBlock(block), context: context); + } + public static bool InlineAllInBlock(Block block, ILTransformContext context) { bool modified = false; @@ -83,20 +88,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Aggressively inlines the stloc instruction at block.Body[pos] into the next instruction, if possible. - /// If inlining was possible; we will continue to inline (non-aggressively) into the the combined instruction. /// - /// - /// After the operation, pos will point to the new combined instruction. - /// - public static bool InlineIfPossible(Block block, ref int pos, ILTransformContext context) + public static bool InlineIfPossible(Block block, int pos, ILTransformContext context) { - if (InlineOneIfPossible(block, pos, true, context)) { - pos -= InlineInto(block, pos, false, context); - return true; - } - return false; + return InlineOneIfPossible(block, pos, true, context); } - + /// /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction, if possible. /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs index 078e1b34b..ab3499fdd 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs @@ -30,19 +30,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// The ?? operator for nullables is handled by NullableLiftingTransform. /// - class NullCoalescingTransform : IBlockTransform + class NullCoalescingTransform : IStatementTransform { - BlockTransformContext context; - - void IBlockTransform.Run(Block block, BlockTransformContext context) + public void Run(Block block, int pos, StatementTransformContext context) { - this.context = context; - for (int i = block.Instructions.Count - 1; i >= 0; i--) { - if (TransformRefTypes(block, i)) { - block.Instructions.RemoveAt(i); - continue; - } - } + TransformRefTypes(block, pos, context); } /// @@ -55,15 +47,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// => /// stloc s(if.notnull(valueInst, fallbackInst)) /// - bool TransformRefTypes(Block block, int i) + bool TransformRefTypes(Block block, int pos, StatementTransformContext context) { - if (i == 0) - return false; - if (!(block.Instructions[i - 1] is StLoc stloc)) + if (!(block.Instructions[pos] is StLoc stloc)) return false; if (stloc.Variable.Kind != VariableKind.StackSlot) return false; - if (!block.Instructions[i].MatchIfInstruction(out var condition, out var trueInst)) + if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) return false; trueInst = Block.Unwrap(trueInst); if (condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(stloc.Variable) && right.MatchLdNull() @@ -71,7 +61,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms ) { context.Step("NullCoalescingTransform (reference types)", stloc); stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, fallbackValue); - return true; // returning true removes the if instruction + block.Instructions.RemoveAt(pos + 1); // remove if instruction + ILInlining.InlineOneIfPossible(block, pos, false, context); + return true; } return false; } diff --git a/ICSharpCode.Decompiler/IL/Transforms/StatementTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/StatementTransform.cs new file mode 100644 index 000000000..40dad287e --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/StatementTransform.cs @@ -0,0 +1,146 @@ +// Copyright (c) 2017 Daniel Grunwald +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Diagnostics; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + /// + /// IL transform that runs on a sequence of statements within a block. + /// + /// + /// Interleaving different statement-combining transforms on a per-statement level + /// improves detection of nested constructs. + /// For example, array initializers can assume that each element assignment was already + /// reduced to a single statement, even if the element contains a high-level construct + /// detected by a different transform (e.g. object initializer). + /// + public interface IStatementTransform + { + /// + /// Runs the transform on the statements within a block. + /// + /// Note: the transform may only modify block.Instructions[pos..]. + /// The transform will be called repeatedly for pos=block.Instructions.Count-1, pos=block.Instructions.Count-2, ..., pos=0. + /// + /// The current block. + /// The starting position where the transform is allowed to work. + /// Additional parameters. + /// + /// Instructions prior to block.Instructions[pos] must not be modified. + /// It is valid to read such instructions, but not recommended as those have not been transformed yet. + /// + void Run(Block block, int pos, StatementTransformContext context); + } + + /// + /// Parameter class holding various arguments for . + /// + public class StatementTransformContext : ILTransformContext + { + public BlockTransformContext BlockContext { get; } + + public StatementTransformContext(BlockTransformContext blockContext) : base(blockContext) + { + this.BlockContext = blockContext ?? throw new ArgumentNullException(nameof(blockContext)); + } + + /// + /// Gets the block on which the transform is running. + /// + public Block Block => BlockContext.Block; + + internal bool rerunCurrentPosition; + internal int? rerunPosition; + + /// + /// After the current statement transform has completed, + /// do not continue with the next statement transform at the same position. + /// Instead, re-run all statement transforms (including the current transform) starting at the specified position. + /// + public void RequestRerun(int pos) + { + if (rerunPosition == null || pos > rerunPosition) { + rerunPosition = pos; + } + } + + /// + /// After the current statement transform has completed, + /// repeat all statement transforms on the current position. + /// + public void RequestRerun() + { + rerunCurrentPosition = true; + } + } + + /// + /// Block transform that runs a list of statement transforms. + /// + public class StatementTransform : IBlockTransform + { + readonly IStatementTransform[] children; + + public StatementTransform(params IStatementTransform[] children) + { + this.children = children; + } + + public void Run(Block block, BlockTransformContext context) + { + var ctx = new StatementTransformContext(context); + int pos = 0; + ctx.rerunPosition = block.Instructions.Count - 1; + while (pos >= 0) { + if (ctx.rerunPosition != null) { + Debug.Assert(ctx.rerunPosition >= pos); +#if DEBUG + for (; pos < ctx.rerunPosition; ++pos) { + block.Instructions[pos].ResetDirty(); + } +#else + pos = ctx.rerunPosition.Value; +#endif + Debug.Assert(pos == ctx.rerunPosition); + ctx.rerunPosition = null; + } + foreach (var transform in children) { + transform.Run(block, pos, ctx); + block.CheckInvariant(ILPhase.Normal); +#if DEBUG + for (int i = 0; i < pos; ++i) { + Debug.Assert(!block.Instructions[i].IsDirty, $"{transform.GetType().Name} modified an instruction before pos"); + } +#endif + if (ctx.rerunCurrentPosition) { + ctx.rerunCurrentPosition = false; + ctx.RequestRerun(pos); + } + if (ctx.rerunPosition != null) { + break; + } + } + if (ctx.rerunPosition == null) { + pos--; + } + } + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs index bbe722027..cda1d926d 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs @@ -29,16 +29,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Transforms array initialization pattern of System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray. /// For collection and object initializers see /// - public class TransformArrayInitializers : IBlockTransform + public class TransformArrayInitializers : IStatementTransform { - BlockTransformContext context; + StatementTransformContext context; - void IBlockTransform.Run(Block block, BlockTransformContext context) + void IStatementTransform.Run(Block block, int pos, StatementTransformContext context) { this.context = context; - for (int i = block.Instructions.Count - 1; i >= 0; i--) { - if (!DoTransform(block, i)) - DoTransformMultiDim(block, i); + try { + if (!DoTransform(block, pos)) + DoTransformMultiDim(block, pos); + } finally { + this.context = null; } } @@ -58,9 +60,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms context.Step("ForwardScanInitializeArrayRuntimeHelper", inst); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); var block = BlockFromInitializer(tempStore, elementType, arrayLength, values); - body.Instructions[pos].ReplaceWith(new StLoc(v, block)); + body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveAt(initArrayPos); - ILInlining.InlineIfPossible(body, ref pos, context); + ILInlining.InlineIfPossible(body, pos, context); return true; } if (arrayLength.Length == 1) { @@ -78,9 +80,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms } )); block.FinalInstruction = new LdLoc(tempStore); - body.Instructions[pos].ReplaceWith(new StLoc(v, block)); - RemoveInstructions(body, pos + 1, instructionsToRemove); - ILInlining.InlineIfPossible(body, ref pos, context); + body.Instructions[pos] = new StLoc(v, block); + body.Instructions.RemoveRange(pos + 1, instructionsToRemove); + ILInlining.InlineIfPossible(body, pos, context); return true; } if (HandleJaggedArrayInitializer(body, pos + 1, v, arrayLength[0], out ILVariable finalStore, out values, out instructionsToRemove)) { @@ -90,9 +92,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms block.Instructions.Add(new StLoc(tempStore, new NewArr(elementType, arrayLength.Select(l => new LdcI4(l)).ToArray()))); block.Instructions.AddRange(values.SelectWithIndex((i, value) => StElem(new LdLoc(tempStore), new[] { new LdcI4(i) }, value, elementType))); block.FinalInstruction = new LdLoc(tempStore); - body.Instructions[pos].ReplaceWith(new StLoc(finalStore, block)); - RemoveInstructions(body, pos + 1, instructionsToRemove); - ILInlining.InlineIfPossible(body, ref pos, context); + body.Instructions[pos] = new StLoc(finalStore, block); + body.Instructions.RemoveRange(pos + 1, instructionsToRemove); + ILInlining.InlineIfPossible(body, pos, context); return true; } } @@ -129,7 +131,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { ITypeDefinition typeDef = elementType.GetEnumUnderlyingType().GetDefinition(); if (typeDef == null) - return new LdNull(); + return new DefaultValue(elementType); switch (typeDef.KnownTypeCode) { case KnownTypeCode.Boolean: case KnownTypeCode.Char: @@ -153,18 +155,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms case KnownTypeCode.IntPtr: case KnownTypeCode.UIntPtr: default: - return new LdNull(); + return new DefaultValue(elementType); } } - - void RemoveInstructions(Block body, int start, int count) - { - for (int i = 0; i < count; i++) { - body.Instructions.RemoveAt(start); - } - } - /// /// Handle simple case where RuntimeHelpers.InitializeArray is not used. /// @@ -259,7 +253,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms var block = BlockFromInitializer(v, arrayType, length, values); body.Instructions[pos].ReplaceWith(new StLoc(v, block)); body.Instructions.RemoveAt(initArrayPos); - ILInlining.InlineIfPossible(body, ref pos, context); + ILInlining.InlineIfPossible(body, pos, context); return true; } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs index 3e6f175f2..ab6873509 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs @@ -26,20 +26,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Transforms collection and object initialization patterns. /// - public class TransformCollectionAndObjectInitializers : IBlockTransform + public class TransformCollectionAndObjectInitializers : IStatementTransform { - BlockTransformContext context; + StatementTransformContext context; - void IBlockTransform.Run(Block block, BlockTransformContext context) + void IStatementTransform.Run(Block block, int pos, StatementTransformContext context) { this.context = context; - for (int i = block.Instructions.Count - 1; i >= 0; i--) - { - DoTransform(block, i); - // This happens in some cases: - // Use correct index after transformation. - if (i >= block.Instructions.Count) - i = block.Instructions.Count; + try { + DoTransform(block, pos); + } finally { + this.context = null; } } @@ -136,9 +133,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } initInst.ReplaceWith(initializerBlock); - for (int i = 0; i < initializerItemsCount; i++) - body.Instructions.RemoveAt(pos + 1); - ILInlining.InlineIfPossible(body, ref pos, context); + body.Instructions.RemoveRange(pos + 1, initializerItemsCount); + ILInlining.InlineIfPossible(body, pos, context); } return true; } diff --git a/ILSpy/Languages/ILAstLanguage.cs b/ILSpy/Languages/ILAstLanguage.cs index d3da19b36..488926a76 100644 --- a/ILSpy/Languages/ILAstLanguage.cs +++ b/ILSpy/Languages/ILAstLanguage.cs @@ -161,9 +161,7 @@ namespace ICSharpCode.ILSpy var reader = new ILReader(specializingTypeSystem); reader.UseDebugSymbols = options.DecompilerSettings.UseDebugSymbols; ILFunction il = reader.ReadIL(method.Body, options.CancellationToken); - ILTransformContext context = new ILTransformContext { - Settings = options.DecompilerSettings, - TypeSystem = typeSystem, + ILTransformContext context = new ILTransformContext(il, typeSystem, options.DecompilerSettings) { CancellationToken = options.CancellationToken }; context.Stepper.StepLimit = options.StepLimit;