From f86bec4e0c4d4e0ca48db16dc1fe587096a1cceb Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 1 Jun 2018 19:05:48 +0200 Subject: [PATCH] Move named arguments to its own transform. Like other statement transforms that build inline blocks, it's important that the named argument transform runs after the ExpressionTransforms. --- .../TestCases/Pretty/NamedArguments.cs | 12 ++ .../TestCases/Pretty/NamedArguments.il | 48 ++++++++ .../TestCases/Pretty/NamedArguments.opt.il | 42 +++++++ .../Pretty/NamedArguments.opt.roslyn.il | 42 +++++++ .../TestCases/Pretty/NamedArguments.roslyn.il | 46 ++++++++ .../CSharp/CSharpDecompiler.cs | 3 +- .../ICSharpCode.Decompiler.csproj | 1 + .../IL/Transforms/ILInlining.cs | 105 ++--------------- .../IL/Transforms/NamedArgumentTransform.cs | 111 ++++++++++++++++++ 9 files changed, 316 insertions(+), 94 deletions(-) create mode 100644 ICSharpCode.Decompiler/IL/Transforms/NamedArgumentTransform.cs diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.cs index f6bc4d7eb..499baef34 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.cs @@ -20,6 +20,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class NamedArguments { + private class ClassWithNamedArgCtor + { + internal ClassWithNamedArgCtor(bool arg1 = false, bool arg2 = false) + { + } + + internal ClassWithNamedArgCtor() + : this(arg2: Get(1) != 1, arg1: Get(2) == 2) + { + } + } + public void Use(int a, int b, int c) { } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.il index ae6d2e2a7..b0ace0e0a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.il @@ -32,6 +32,54 @@ .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments extends [mscorlib]System.Object { + .class auto ansi nested private beforefieldinit ClassWithNamedArgCtor + extends [mscorlib]System.Object + { + .method assembly hidebysig specialname rtspecialname + instance void .ctor([opt] bool arg1, + [opt] bool arg2) cil managed + { + .param [1] = bool(false) + .param [2] = bool(false) + // Code size 10 (0xa) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: nop + IL_0008: nop + IL_0009: ret + } // end of method ClassWithNamedArgCtor::.ctor + + .method assembly hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 33 (0x21) + .maxstack 3 + .locals init (bool V_0) + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments::Get(int32) + IL_0007: ldc.i4.1 + IL_0008: ceq + IL_000a: ldc.i4.0 + IL_000b: ceq + IL_000d: stloc.0 + IL_000e: ldc.i4.2 + IL_000f: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments::Get(int32) + IL_0014: ldc.i4.2 + IL_0015: ceq + IL_0017: ldloc.0 + IL_0018: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments/ClassWithNamedArgCtor::.ctor(bool, + bool) + IL_001d: nop + IL_001e: nop + IL_001f: nop + IL_0020: ret + } // end of method ClassWithNamedArgCtor::.ctor + + } // end of class ClassWithNamedArgCtor + .method public hidebysig instance void Use(int32 a, int32 b, diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.opt.il index efb47ca67..8aea36a8a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.opt.il @@ -32,6 +32,48 @@ .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments extends [mscorlib]System.Object { + .class auto ansi nested private beforefieldinit ClassWithNamedArgCtor + extends [mscorlib]System.Object + { + .method assembly hidebysig specialname rtspecialname + instance void .ctor([opt] bool arg1, + [opt] bool arg2) cil managed + { + .param [1] = bool(false) + .param [2] = bool(false) + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method ClassWithNamedArgCtor::.ctor + + .method assembly hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 30 (0x1e) + .maxstack 3 + .locals init (bool V_0) + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments::Get(int32) + IL_0007: ldc.i4.1 + IL_0008: ceq + IL_000a: ldc.i4.0 + IL_000b: ceq + IL_000d: stloc.0 + IL_000e: ldc.i4.2 + IL_000f: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments::Get(int32) + IL_0014: ldc.i4.2 + IL_0015: ceq + IL_0017: ldloc.0 + IL_0018: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments/ClassWithNamedArgCtor::.ctor(bool, + bool) + IL_001d: ret + } // end of method ClassWithNamedArgCtor::.ctor + + } // end of class ClassWithNamedArgCtor + .method public hidebysig instance void Use(int32 a, int32 b, diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.opt.roslyn.il index 86b33e26d..fa62bac33 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.opt.roslyn.il @@ -36,6 +36,48 @@ .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments extends [mscorlib]System.Object { + .class auto ansi nested private beforefieldinit ClassWithNamedArgCtor + extends [mscorlib]System.Object + { + .method assembly hidebysig specialname rtspecialname + instance void .ctor([opt] bool arg1, + [opt] bool arg2) cil managed + { + .param [1] = bool(false) + .param [2] = bool(false) + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method ClassWithNamedArgCtor::.ctor + + .method assembly hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 30 (0x1e) + .maxstack 3 + .locals init (bool V_0) + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments::Get(int32) + IL_0007: ldc.i4.1 + IL_0008: ceq + IL_000a: ldc.i4.0 + IL_000b: ceq + IL_000d: stloc.0 + IL_000e: ldc.i4.2 + IL_000f: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments::Get(int32) + IL_0014: ldc.i4.2 + IL_0015: ceq + IL_0017: ldloc.0 + IL_0018: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments/ClassWithNamedArgCtor::.ctor(bool, + bool) + IL_001d: ret + } // end of method ClassWithNamedArgCtor::.ctor + + } // end of class ClassWithNamedArgCtor + .method public hidebysig instance void Use(int32 a, int32 b, diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.roslyn.il index 0b3206cd4..466082996 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.roslyn.il @@ -36,6 +36,52 @@ .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments extends [mscorlib]System.Object { + .class auto ansi nested private beforefieldinit ClassWithNamedArgCtor + extends [mscorlib]System.Object + { + .method assembly hidebysig specialname rtspecialname + instance void .ctor([opt] bool arg1, + [opt] bool arg2) cil managed + { + .param [1] = bool(false) + .param [2] = bool(false) + // Code size 9 (0x9) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: nop + IL_0008: ret + } // end of method ClassWithNamedArgCtor::.ctor + + .method assembly hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 32 (0x20) + .maxstack 3 + .locals init (bool V_0) + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments::Get(int32) + IL_0007: ldc.i4.1 + IL_0008: ceq + IL_000a: ldc.i4.0 + IL_000b: ceq + IL_000d: stloc.0 + IL_000e: ldc.i4.2 + IL_000f: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments::Get(int32) + IL_0014: ldc.i4.2 + IL_0015: ceq + IL_0017: ldloc.0 + IL_0018: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments/ClassWithNamedArgCtor::.ctor(bool, + bool) + IL_001d: nop + IL_001e: nop + IL_001f: ret + } // end of method ClassWithNamedArgCtor::.ctor + + } // end of class ClassWithNamedArgCtor + .method public hidebysig instance void Use(int32 a, int32 b, diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 3667696c2..c84fd041b 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -133,7 +133,8 @@ namespace ICSharpCode.Decompiler.CSharp new NullPropagationStatementTransform(), new TransformArrayInitializers(), new TransformCollectionAndObjectInitializers(), - new TransformExpressionTrees() + new TransformExpressionTrees(), + new NamedArgumentTransform() ), } }, diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index cacda3343..eef45e352 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -297,6 +297,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 2c1feee87..8dc6aa802 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -51,13 +51,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms } public void Run(Block block, int pos, StatementTransformContext context) + { + InlineOneIfPossible(block, pos, OptionsForBlock(block), context: context); + } + + internal static InliningOptions OptionsForBlock(Block block) { InliningOptions options = InliningOptions.None; if (IsCatchWhenBlock(block)) options |= InliningOptions.Aggressive; - if (context.Settings.NamedArguments) - options |= InliningOptions.IntroduceNamedArguments; - InlineOneIfPossible(block, pos, options, context: context); + return options; } public static bool InlineAllInBlock(ILFunction function, Block block, ILTransformContext context) @@ -234,31 +237,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms } return true; } else if (r == FindResult.NamedArgument && (options & InliningOptions.IntroduceNamedArguments) != 0) { - Debug.Assert(loadInst.OpCode == OpCode.LdLoc); - StLoc originalStore = (StLoc)inlinedExpression.Parent; - if ((options & InliningOptions.Aggressive) == 0 && originalStore.ILStackWasEmpty) - return false; - context.Step($"Introduce named argument '{v.Name}'", inlinedExpression); - var call = (CallInstruction)loadInst.Parent; - if (!(call.Parent is Block namedArgBlock) || namedArgBlock.Kind != BlockKind.CallWithNamedArgs) { - // create namedArgBlock: - namedArgBlock = new Block(BlockKind.CallWithNamedArgs); - call.ReplaceWith(namedArgBlock); - namedArgBlock.FinalInstruction = call; - if (call.IsInstanceCall) { - IType thisVarType = call.Method.DeclaringType; - if (CallInstruction.ExpectedTypeForThisPointer(thisVarType) == StackType.Ref) { - thisVarType = new ByReferenceType(thisVarType); - } - var function = call.Ancestors.OfType().First(); - var thisArgVar = function.RegisterVariable(VariableKind.NamedArgument, thisVarType, "this_arg"); - namedArgBlock.Instructions.Add(new StLoc(thisArgVar, call.Arguments[0])); - call.Arguments[0] = new LdLoc(thisArgVar); - } - } - v.Kind = VariableKind.NamedArgument; - namedArgBlock.Instructions.Insert(call.IsInstanceCall ? 1 : 0, originalStore); - return true; + return NamedArgumentTransform.DoInline(v, (StLoc)inlinedExpression.Parent, (LdLoc)loadInst, + options, context); } return false; } @@ -400,7 +380,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return FindLoadInNext(expr, v, expressionBeingMoved, out _) == FindResult.Found; } - enum FindResult + internal enum FindResult { /// /// Found a load; inlining is possible. @@ -427,7 +407,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Finds the position to inline to. /// /// true = found; false = cannot continue search; null = not found - static FindResult FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, out ILInstruction loadInst) + internal static FindResult FindLoadInNext(ILInstruction expr, ILVariable v, ILInstruction expressionBeingMoved, out ILInstruction loadInst) { loadInst = null; if (expr == null) @@ -449,7 +429,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // If FindLoadInNext() returns null, we still can't continue searching // because we can't inline over the remainder of the block. case BlockKind.CallWithNamedArgs: - return CanExtendNamedArgument(block, v, expressionBeingMoved, out loadInst); + return NamedArgumentTransform.CanExtendNamedArgument(block, v, expressionBeingMoved, out loadInst); default: return FindResult.Stop; } @@ -472,7 +452,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms FindResult r = FindLoadInNext(child, v, expressionBeingMoved, out loadInst); if (r != FindResult.Continue) { if (r == FindResult.Stop && expr is CallInstruction call) - return CanIntroduceNamedArgument(call, child, v, out loadInst); + return NamedArgumentTransform.CanIntroduceNamedArgument(call, child, v, out loadInst); return r; } } @@ -490,67 +470,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms return findResult; } - private static FindResult CanIntroduceNamedArgument(CallInstruction call, ILInstruction child, ILVariable v, out ILInstruction loadInst) - { - loadInst = null; - Debug.Assert(child.Parent == call); - if (call.IsInstanceCall && child.ChildIndex == 0) - return FindResult.Stop; // cannot use named arg to move expressionBeingMoved before this pointer - if (call.Method.IsOperator || call.Method.IsAccessor) - return FindResult.Stop; // cannot use named arg for operators or accessors - if (call.Method is VarArgInstanceMethod) - return FindResult.Stop; // CallBuilder doesn't support named args when using varargs - if (call.Method.IsConstructor) { - IType type = call.Method.DeclaringType; - if (type.Kind == TypeKind.Delegate || type.IsAnonymousType()) - return FindResult.Stop; - } - if (call.Method.Parameters.Any(p => string.IsNullOrEmpty(p.Name))) - return FindResult.Stop; // cannot use named arguments - for (int i = child.ChildIndex; i < call.Arguments.Count; i++) { - if (call.Arguments[i] is LdLoc ldloc && ldloc.Variable == v) { - loadInst = ldloc; - return FindResult.NamedArgument; - } - } - return FindResult.Stop; - } - - private static FindResult CanExtendNamedArgument(Block block, ILVariable v, ILInstruction expressionBeingMoved, out ILInstruction loadInst) - { - Debug.Assert(block.Kind == BlockKind.CallWithNamedArgs); - var firstArg = ((StLoc)block.Instructions[0]).Value; - var r = FindLoadInNext(firstArg, v, expressionBeingMoved, out loadInst); - if (r == FindResult.Found || r == FindResult.NamedArgument) { - return r; // OK, inline into first instruction of block - } - var call = (CallInstruction)block.FinalInstruction; - if (call.IsInstanceCall) { - // For instance calls, block.Instructions[0] is the argument - // for the 'this' pointer. We can only insert at position 1. - if (r == FindResult.Stop) { - // error: can't move expressionBeingMoved after block.Instructions[0] - return FindResult.Stop; - } - // Because we always ensure block.Instructions[0] is the 'this' argument, - // it's possible that the place we actually need to inline into - // is within block.Instructions[1]: - if (block.Instructions.Count > 1) { - r = FindLoadInNext(block.Instructions[1], v, expressionBeingMoved, out loadInst); - if (r == FindResult.Found || r == FindResult.NamedArgument) { - return r; // OK, inline into block.Instructions[1] - } - } - } - foreach (var arg in call.Arguments) { - if (arg.MatchLdLoc(v)) { - loadInst = arg; - return FindResult.NamedArgument; - } - } - return FindResult.Stop; - } - /// /// Determines whether it is safe to move 'expressionBeingMoved' past 'expr' /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/NamedArgumentTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NamedArgumentTransform.cs new file mode 100644 index 000000000..a9627c68b --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/NamedArgumentTransform.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using ICSharpCode.Decompiler.TypeSystem; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + using FindResult = ILInlining.FindResult; + + class NamedArgumentTransform : IStatementTransform + { + public static FindResult CanIntroduceNamedArgument(CallInstruction call, ILInstruction child, ILVariable v, out ILInstruction loadInst) + { + loadInst = null; + Debug.Assert(child.Parent == call); + if (call.IsInstanceCall && child.ChildIndex == 0) + return FindResult.Stop; // cannot use named arg to move expressionBeingMoved before this pointer + if (call.Method.IsOperator || call.Method.IsAccessor) + return FindResult.Stop; // cannot use named arg for operators or accessors + if (call.Method is VarArgInstanceMethod) + return FindResult.Stop; // CallBuilder doesn't support named args when using varargs + if (call.Method.IsConstructor) { + IType type = call.Method.DeclaringType; + if (type.Kind == TypeKind.Delegate || type.IsAnonymousType()) + return FindResult.Stop; + } + if (call.Method.Parameters.Any(p => string.IsNullOrEmpty(p.Name))) + return FindResult.Stop; // cannot use named arguments + for (int i = child.ChildIndex; i < call.Arguments.Count; i++) { + if (call.Arguments[i] is LdLoc ldloc && ldloc.Variable == v) { + loadInst = ldloc; + return FindResult.NamedArgument; + } + } + return FindResult.Stop; + } + + internal static FindResult CanExtendNamedArgument(Block block, ILVariable v, ILInstruction expressionBeingMoved, out ILInstruction loadInst) + { + Debug.Assert(block.Kind == BlockKind.CallWithNamedArgs); + var firstArg = ((StLoc)block.Instructions[0]).Value; + var r = ILInlining.FindLoadInNext(firstArg, v, expressionBeingMoved, out loadInst); + if (r == FindResult.Found || r == FindResult.NamedArgument) { + return r; // OK, inline into first instruction of block + } + var call = (CallInstruction)block.FinalInstruction; + if (call.IsInstanceCall) { + // For instance calls, block.Instructions[0] is the argument + // for the 'this' pointer. We can only insert at position 1. + if (r == FindResult.Stop) { + // error: can't move expressionBeingMoved after block.Instructions[0] + return FindResult.Stop; + } + // Because we always ensure block.Instructions[0] is the 'this' argument, + // it's possible that the place we actually need to inline into + // is within block.Instructions[1]: + if (block.Instructions.Count > 1) { + r = ILInlining.FindLoadInNext(block.Instructions[1], v, expressionBeingMoved, out loadInst); + if (r == FindResult.Found || r == FindResult.NamedArgument) { + return r; // OK, inline into block.Instructions[1] + } + } + } + foreach (var arg in call.Arguments) { + if (arg.MatchLdLoc(v)) { + loadInst = arg; + return FindResult.NamedArgument; + } + } + return FindResult.Stop; + } + + internal static bool DoInline(ILVariable v, StLoc originalStore, LdLoc loadInst, InliningOptions options, ILTransformContext context) + { + if ((options & InliningOptions.Aggressive) == 0 && originalStore.ILStackWasEmpty) + return false; + context.Step($"Introduce named argument '{v.Name}'", originalStore); + var call = (CallInstruction)loadInst.Parent; + if (!(call.Parent is Block namedArgBlock) || namedArgBlock.Kind != BlockKind.CallWithNamedArgs) { + // create namedArgBlock: + namedArgBlock = new Block(BlockKind.CallWithNamedArgs); + call.ReplaceWith(namedArgBlock); + namedArgBlock.FinalInstruction = call; + if (call.IsInstanceCall) { + IType thisVarType = call.Method.DeclaringType; + if (CallInstruction.ExpectedTypeForThisPointer(thisVarType) == StackType.Ref) { + thisVarType = new ByReferenceType(thisVarType); + } + var function = call.Ancestors.OfType().First(); + var thisArgVar = function.RegisterVariable(VariableKind.NamedArgument, thisVarType, "this_arg"); + namedArgBlock.Instructions.Add(new StLoc(thisArgVar, call.Arguments[0])); + call.Arguments[0] = new LdLoc(thisArgVar); + } + } + v.Kind = VariableKind.NamedArgument; + namedArgBlock.Instructions.Insert(call.IsInstanceCall ? 1 : 0, originalStore); + return true; + } + + public void Run(Block block, int pos, StatementTransformContext context) + { + if (!context.Settings.NamedArguments) + return; + var options = ILInlining.OptionsForBlock(block); + options |= InliningOptions.IntroduceNamedArguments; + ILInlining.InlineOneIfPossible(block, pos, options, context: context); + } + } +}