Browse Source

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.
pull/1167/head
Daniel Grunwald 7 years ago
parent
commit
f86bec4e0c
  1. 12
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.cs
  2. 48
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.il
  3. 42
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.opt.il
  4. 42
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.opt.roslyn.il
  5. 46
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.roslyn.il
  6. 3
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  7. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  8. 105
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  9. 111
      ICSharpCode.Decompiler/IL/Transforms/NamedArgumentTransform.cs

12
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.cs

@ -20,6 +20,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
public class NamedArguments 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) public void Use(int a, int b, int c)
{ {
} }

48
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.il

@ -32,6 +32,54 @@
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments
extends [mscorlib]System.Object 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 .method public hidebysig instance void
Use(int32 a, Use(int32 a,
int32 b, int32 b,

42
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.opt.il

@ -32,6 +32,48 @@
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments
extends [mscorlib]System.Object 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 .method public hidebysig instance void
Use(int32 a, Use(int32 a,
int32 b, int32 b,

42
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.opt.roslyn.il

@ -36,6 +36,48 @@
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments
extends [mscorlib]System.Object 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 .method public hidebysig instance void
Use(int32 a, Use(int32 a,
int32 b, int32 b,

46
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NamedArguments.roslyn.il

@ -36,6 +36,52 @@
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments .class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NamedArguments
extends [mscorlib]System.Object 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 .method public hidebysig instance void
Use(int32 a, Use(int32 a,
int32 b, int32 b,

3
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -133,7 +133,8 @@ namespace ICSharpCode.Decompiler.CSharp
new NullPropagationStatementTransform(), new NullPropagationStatementTransform(),
new TransformArrayInitializers(), new TransformArrayInitializers(),
new TransformCollectionAndObjectInitializers(), new TransformCollectionAndObjectInitializers(),
new TransformExpressionTrees() new TransformExpressionTrees(),
new NamedArgumentTransform()
), ),
} }
}, },

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -297,6 +297,7 @@
<Compile Include="IL\Transforms\EarlyExpressionTransforms.cs" /> <Compile Include="IL\Transforms\EarlyExpressionTransforms.cs" />
<Compile Include="IL\Transforms\ExpressionTreeCast.cs" /> <Compile Include="IL\Transforms\ExpressionTreeCast.cs" />
<Compile Include="IL\Transforms\HighLevelLoopTransform.cs" /> <Compile Include="IL\Transforms\HighLevelLoopTransform.cs" />
<Compile Include="IL\Transforms\NamedArgumentTransform.cs" />
<Compile Include="IL\Transforms\NullPropagationTransform.cs" /> <Compile Include="IL\Transforms\NullPropagationTransform.cs" />
<Compile Include="IL\Transforms\ProxyCallReplacer.cs" /> <Compile Include="IL\Transforms\ProxyCallReplacer.cs" />
<Compile Include="IL\Instructions\StringToInt.cs" /> <Compile Include="IL\Instructions\StringToInt.cs" />

105
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -51,13 +51,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
public void Run(Block block, int pos, StatementTransformContext context) 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; InliningOptions options = InliningOptions.None;
if (IsCatchWhenBlock(block)) if (IsCatchWhenBlock(block))
options |= InliningOptions.Aggressive; options |= InliningOptions.Aggressive;
if (context.Settings.NamedArguments) return options;
options |= InliningOptions.IntroduceNamedArguments;
InlineOneIfPossible(block, pos, options, context: context);
} }
public static bool InlineAllInBlock(ILFunction function, Block block, ILTransformContext context) public static bool InlineAllInBlock(ILFunction function, Block block, ILTransformContext context)
@ -234,31 +237,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
return true; return true;
} else if (r == FindResult.NamedArgument && (options & InliningOptions.IntroduceNamedArguments) != 0) { } else if (r == FindResult.NamedArgument && (options & InliningOptions.IntroduceNamedArguments) != 0) {
Debug.Assert(loadInst.OpCode == OpCode.LdLoc); return NamedArgumentTransform.DoInline(v, (StLoc)inlinedExpression.Parent, (LdLoc)loadInst,
StLoc originalStore = (StLoc)inlinedExpression.Parent; options, context);
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<ILFunction>().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 false; return false;
} }
@ -400,7 +380,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return FindLoadInNext(expr, v, expressionBeingMoved, out _) == FindResult.Found; return FindLoadInNext(expr, v, expressionBeingMoved, out _) == FindResult.Found;
} }
enum FindResult internal enum FindResult
{ {
/// <summary> /// <summary>
/// Found a load; inlining is possible. /// Found a load; inlining is possible.
@ -427,7 +407,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Finds the position to inline to. /// Finds the position to inline to.
/// </summary> /// </summary>
/// <returns>true = found; false = cannot continue search; null = not found</returns> /// <returns>true = found; false = cannot continue search; null = not found</returns>
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; loadInst = null;
if (expr == null) if (expr == null)
@ -449,7 +429,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// If FindLoadInNext() returns null, we still can't continue searching // If FindLoadInNext() returns null, we still can't continue searching
// because we can't inline over the remainder of the block. // because we can't inline over the remainder of the block.
case BlockKind.CallWithNamedArgs: case BlockKind.CallWithNamedArgs:
return CanExtendNamedArgument(block, v, expressionBeingMoved, out loadInst); return NamedArgumentTransform.CanExtendNamedArgument(block, v, expressionBeingMoved, out loadInst);
default: default:
return FindResult.Stop; return FindResult.Stop;
} }
@ -472,7 +452,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
FindResult r = FindLoadInNext(child, v, expressionBeingMoved, out loadInst); FindResult r = FindLoadInNext(child, v, expressionBeingMoved, out loadInst);
if (r != FindResult.Continue) { if (r != FindResult.Continue) {
if (r == FindResult.Stop && expr is CallInstruction call) 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; return r;
} }
} }
@ -490,67 +470,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return findResult; 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;
}
/// <summary> /// <summary>
/// Determines whether it is safe to move 'expressionBeingMoved' past 'expr' /// Determines whether it is safe to move 'expressionBeingMoved' past 'expr'
/// </summary> /// </summary>

111
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<ILFunction>().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);
}
}
}
Loading…
Cancel
Save