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;