Browse Source

Merge Return instruction into Leave.

pull/863/head
Siegfried Pammer 8 years ago
parent
commit
206cdecf30
  1. 13
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  2. 7
      ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs
  3. 8
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  4. 20
      ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowGraph.cs
  5. 18
      ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs
  6. 5
      ICSharpCode.Decompiler/IL/ControlFlow/ExitPoints.cs
  7. 31
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  8. 3
      ICSharpCode.Decompiler/IL/ILReader.cs
  9. 172
      ICSharpCode.Decompiler/IL/Instructions.cs
  10. 6
      ICSharpCode.Decompiler/IL/Instructions.tt
  11. 10
      ICSharpCode.Decompiler/IL/Instructions/Leave.cs
  12. 30
      ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
  13. 2
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
  14. 2
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

13
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -161,7 +161,10 @@ namespace ICSharpCode.Decompiler.CSharp
if (inst.IsLeavingFunction) { if (inst.IsLeavingFunction) {
if (currentFunction.IsIterator) if (currentFunction.IsIterator)
return new YieldBreakStatement(); return new YieldBreakStatement();
else else if (!inst.Value.MatchNop()) {
IType targetType = currentFunction.IsAsync ? currentFunction.AsyncReturnType : currentMethod.ReturnType;
return new ReturnStatement(exprBuilder.Translate(inst.Value).ConvertTo(targetType, exprBuilder, allowImplicitConversion: true));
} else
return new ReturnStatement(); return new ReturnStatement();
} }
string label; string label;
@ -181,12 +184,6 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
return new ThrowStatement(); return new ThrowStatement();
} }
protected internal override Statement VisitReturn(Return inst)
{
IType targetType = currentFunction.IsAsync ? currentFunction.AsyncReturnType : currentMethod.ReturnType;
return new ReturnStatement(exprBuilder.Translate(inst.Value).ConvertTo(targetType, exprBuilder, allowImplicitConversion: true));
}
protected internal override Statement VisitYieldReturn(YieldReturn inst) protected internal override Statement VisitYieldReturn(YieldReturn inst)
{ {
@ -420,6 +417,8 @@ namespace ICSharpCode.Decompiler.CSharp
static bool IsFinalLeave(Leave leave) static bool IsFinalLeave(Leave leave)
{ {
if (!leave.Value.MatchNop())
return false;
Block block = (Block)leave.Parent; Block block = (Block)leave.Parent;
if (leave.ChildIndex != block.Instructions.Count - 1 || block.FinalInstruction.OpCode != OpCode.Nop) if (leave.ChildIndex != block.Instructions.Count - 1 || block.FinalInstruction.OpCode != OpCode.Nop)
return false; return false;

7
ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs

@ -416,6 +416,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
protected internal override void VisitLeave(Leave inst) protected internal override void VisitLeave(Leave inst)
{ {
inst.Value.AcceptVisitor(this);
State targetState; State targetState;
if (stateOnLeave.TryGetValue(inst.TargetContainer, out targetState)) { if (stateOnLeave.TryGetValue(inst.TargetContainer, out targetState)) {
targetState.JoinWith(state); targetState.JoinWith(state);
@ -428,12 +429,6 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
MarkUnreachable(); MarkUnreachable();
} }
protected internal override void VisitReturn(Return inst)
{
inst.Value.AcceptVisitor(this);
MarkUnreachable();
}
protected internal override void VisitThrow(Throw inst) protected internal override void VisitThrow(Throw inst)
{ {
inst.Argument.AcceptVisitor(this); inst.Argument.AcceptVisitor(this);

8
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -447,10 +447,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
moveNextFunction.ReleaseRef(); moveNextFunction.ReleaseRef();
foreach (var branch in function.Descendants.OfType<Branch>()) { foreach (var branch in function.Descendants.OfType<Branch>()) {
if (branch.TargetBlock == setResultAndExitBlock) { if (branch.TargetBlock == setResultAndExitBlock) {
if (resultVar != null) branch.ReplaceWith(new Leave((BlockContainer)function.Body) {
branch.ReplaceWith(new Return(new LdLoc(resultVar)) { ILRange = branch.ILRange }); Value = resultVar == null ? (ILInstruction)new Nop() : new LdLoc(resultVar),
else ILRange = branch.ILRange
branch.ReplaceWith(new Leave((BlockContainer)function.Body) { ILRange = branch.ILRange }); });
} }
} }
function.Variables.AddRange(function.Descendants.OfType<IInstructionWithVariableOperand>().Select(inst => inst.Variable).Distinct()); function.Variables.AddRange(function.Descendants.OfType<IInstructionWithVariableOperand>().Select(inst => inst.Variable).Distinct());

20
ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowGraph.cs

@ -100,9 +100,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// Leave instructions (like other exits out of the container) // Leave instructions (like other exits out of the container)
// are ignored for the CFG and dominance, // are ignored for the CFG and dominance,
// but is relevant for HasReachableExit(). // but is relevant for HasReachableExit().
// However, a 'leave' that exits the whole function represents a void return, // However, a 'leave' that exits the whole function represents a return,
// and is not considered a reachable exit (just like non-void returns). // and is not considered a reachable exit.
if (!(leave.TargetContainer.Parent is ILFunction)) { if (!leave.IsLeavingFunction) {
nodeHasDirectExitOutOfContainer.Set(i); nodeHasDirectExitOutOfContainer.Set(i);
} }
} }
@ -131,20 +131,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return leaving; return leaving;
} }
bool LeavesCurrentBlockContainer(Block block)
{
foreach (var node in block.Descendants) {
if (node is Branch branch && !branch.TargetBlock.IsDescendantOf(container)) {
// control flow that isn't internal to the block container
return true;
}
if (node is Leave leave && !leave.TargetContainer.IsDescendantOf(block)) {
return true;
}
}
return false;
}
/// <summary> /// <summary>
/// Gets the ControlFlowNode for the block. /// Gets the ControlFlowNode for the block.
/// ///

18
ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs

@ -64,13 +64,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// (where 'v' has no other uses) // (where 'v' has no other uses)
// Simplify these to a simple `ret(<inst>)` so that they match the release build version. // Simplify these to a simple `ret(<inst>)` so that they match the release build version.
// //
if (block.Instructions.Count == 2 && block.Instructions[1].OpCode == OpCode.Return) { if (block.Instructions.Count == 2 && block.Instructions[1].MatchReturn(out ILInstruction value)) {
Return ret = (Return)block.Instructions[1]; var ret = (Leave)block.Instructions[1];
ILVariable v; if (value.MatchLdLoc(out ILVariable v)
ILInstruction inst; && v.IsSingleDefinition && v.LoadCount == 1 && block.Instructions[0].MatchStLoc(v, out ILInstruction inst)) {
if (ret.Value.MatchLdLoc(out v)
&& v.IsSingleDefinition && v.LoadCount == 1 && block.Instructions[0].MatchStLoc(v, out inst))
{
context.Step("Inline variable in return block", block); context.Step("Inline variable in return block", block);
inst.AddILRange(ret.Value.ILRange); inst.AddILRange(ret.Value.ILRange);
inst.AddILRange(block.Instructions[0].ILRange); inst.AddILRange(block.Instructions[0].ILRange);
@ -119,11 +116,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
blocksToAdd.Add((localContainer, blockCopy)); blocksToAdd.Add((localContainer, blockCopy));
branch.TargetBlock = blockCopy; branch.TargetBlock = blockCopy;
} }
} else if (targetBlock.Instructions.Count == 1 && targetBlock.Instructions[0].OpCode == OpCode.Leave) { } else if (targetBlock.Instructions.Count == 1 && targetBlock.Instructions[0] is Leave leave && leave.Value.MatchNop()) {
context.Step("Replace branch to leave with leave", branch); context.Step("Replace branch to leave with leave", branch);
// Replace branches to 'leave' instruction with the leave instruction // Replace branches to 'leave' instruction with the leave instruction
Leave leave = (Leave)targetBlock.Instructions[0]; branch.ReplaceWith(leave.Clone());
branch.ReplaceWith(new Leave(leave.TargetContainer) { ILRange = branch.ILRange });
} }
if (targetBlock.IncomingEdgeCount == 0) if (targetBlock.IncomingEdgeCount == 0)
targetBlock.Instructions.Clear(); // mark the block for deletion targetBlock.Instructions.Clear(); // mark the block for deletion
@ -154,7 +150,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
var targetBlock = branch.TargetBlock; var targetBlock = branch.TargetBlock;
if (targetBlock.Instructions.Count != 1 || targetBlock.FinalInstruction.OpCode != OpCode.Nop) if (targetBlock.Instructions.Count != 1 || targetBlock.FinalInstruction.OpCode != OpCode.Nop)
return false; return false;
return targetBlock.Instructions[0] is Return ret && ret.Value is LdLoc; return targetBlock.Instructions[0].MatchReturn(out var value) && value is LdLoc;
} }
static bool CombineBlockWithNextBlock(BlockContainer container, Block block) static bool CombineBlockWithNextBlock(BlockContainer container, Block block)

5
ICSharpCode.Decompiler/IL/ControlFlow/ExitPoints.cs

@ -95,7 +95,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
case OpCode.Leave: case OpCode.Leave:
Leave leave1 = (Leave)exit1; Leave leave1 = (Leave)exit1;
Leave leave2 = (Leave)exit2; Leave leave2 = (Leave)exit2;
return leave1.TargetContainer == leave2.TargetContainer; return leave1.TargetContainer == leave2.TargetContainer && leave1.Value.MatchNop() && leave2.Value.MatchNop();
default: default:
return false; return false;
} }
@ -192,6 +192,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
protected internal override void VisitLeave(Leave inst) protected internal override void VisitLeave(Leave inst)
{ {
base.VisitLeave(inst);
if (!inst.Value.MatchNop())
return;
HandleExit(inst); HandleExit(inst);
} }
} }

31
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -747,23 +747,22 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} }
break; break;
case Leave leave: case Leave leave:
if (leave.TargetContainer == oldBody) { if (leave.MatchReturn(out var value)) {
leave.TargetContainer = newBody; if (value.MatchLdLoc(out var v) && v.IsSingleDefinition
} && v.StoreInstructions.SingleOrDefault() is StLoc stloc) {
break; returnStores.Add(stloc);
case Return ret: value = stloc.Value;
ILInstruction value = ret.Value; }
if (value.MatchLdLoc(out var v) && v.IsSingleDefinition if (value.MatchLdcI4(0)) {
&& v.StoreInstructions.SingleOrDefault() is StLoc stloc) // yield break
{ leave.ReplaceWith(new Leave(newBody) { ILRange = leave.ILRange });
returnStores.Add(stloc); } else {
value = stloc.Value; leave.ReplaceWith(new InvalidBranch("Unexpected return in MoveNext()") { ILRange = leave.ILRange });
} }
if (value.MatchLdcI4(0)) {
// yield break
ret.ReplaceWith(new Leave(newBody) { ILRange = ret.ILRange });
} else { } else {
ret.ReplaceWith(new InvalidBranch("Unexpected return in MoveNext()") { ILRange = ret.ILRange }); if (leave.TargetContainer == oldBody) {
leave.TargetContainer = newBody;
}
} }
break; break;
} }

3
ICSharpCode.Decompiler/IL/ILReader.cs

@ -526,6 +526,7 @@ namespace ICSharpCode.Decompiler.IL
case Cil.Code.Dup: case Cil.Code.Dup:
return Push(Peek()); return Push(Peek());
case Cil.Code.Endfilter: case Cil.Code.Endfilter:
return new Leave(null) { Value = Pop() };
case Cil.Code.Endfinally: case Cil.Code.Endfinally:
return new Leave(null); return new Leave(null);
case Cil.Code.Initblk: case Cil.Code.Initblk:
@ -942,7 +943,7 @@ namespace ICSharpCode.Decompiler.IL
if (methodReturnStackType == StackType.Void) if (methodReturnStackType == StackType.Void)
return new IL.Leave(mainContainer); return new IL.Leave(mainContainer);
else else
return new IL.Return(Pop(methodReturnStackType)); return new IL.Leave(mainContainer) { Value = Pop(methodReturnStackType) };
} }
private ILInstruction DecodeLdstr() private ILInstruction DecodeLdstr()

172
ICSharpCode.Decompiler/IL/Instructions.cs

@ -55,7 +55,7 @@ namespace ICSharpCode.Decompiler.IL
Arglist, Arglist,
/// <summary>Unconditional branch. <c>goto target;</c></summary> /// <summary>Unconditional branch. <c>goto target;</c></summary>
Branch, Branch,
/// <summary>Unconditional branch to end of block container. <c>goto container_end;</c>, often <c>break;</c></summary> /// <summary>Unconditional branch to end of block container. Return is represented using IsLeavingFunction and an (optional) return value. The block container evaluates to the value produced by the argument of the leave instruction.</summary>
Leave, Leave,
/// <summary>If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c></summary> /// <summary>If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c></summary>
IfInstruction, IfInstruction,
@ -115,8 +115,6 @@ namespace ICSharpCode.Decompiler.IL
LdMemberToken, LdMemberToken,
/// <summary>Allocates space in the stack frame</summary> /// <summary>Allocates space in the stack frame</summary>
LocAlloc, LocAlloc,
/// <summary>Returns from the current method or lambda. Only used when returning a value; void returns are represented using a 'leave' instruction.</summary>
Return,
/// <summary>Load address of instance field</summary> /// <summary>Load address of instance field</summary>
LdFlda, LdFlda,
/// <summary>Load static field address</summary> /// <summary>Load static field address</summary>
@ -1060,9 +1058,56 @@ namespace ICSharpCode.Decompiler.IL
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{ {
/// <summary>Unconditional branch to end of block container. <c>goto container_end;</c>, often <c>break;</c></summary> /// <summary>Unconditional branch to end of block container. Return is represented using IsLeavingFunction and an (optional) return value. The block container evaluates to the value produced by the argument of the leave instruction.</summary>
public sealed partial class Leave : SimpleInstruction public sealed partial class Leave : ILInstruction
{ {
public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true);
ILInstruction value;
public ILInstruction Value {
get { return this.value; }
set {
ValidateChild(value);
SetChildInstruction(ref this.value, value, 0);
}
}
protected sealed override int GetChildCount()
{
return 1;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
case 0:
return this.value;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
case 0:
this.Value = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return ValueSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone()
{
var clone = (Leave)ShallowClone();
clone.Value = this.value.Clone();
return clone;
}
public override StackType ResultType { get { return StackType.Void; } } public override StackType ResultType { get { return StackType.Void; } }
public override void AcceptVisitor(ILVisitor visitor) public override void AcceptVisitor(ILVisitor visitor)
{ {
@ -1079,7 +1124,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{ {
var o = other as Leave; var o = other as Leave;
return o != null && this.TargetContainer == o.TargetContainer; return o != null && this.value.PerformMatch(o.value, ref match) && this.TargetContainer == o.TargetContainer;
} }
} }
} }
@ -2538,98 +2583,6 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{
/// <summary>Returns from the current method or lambda. Only used when returning a value; void returns are represented using a 'leave' instruction.</summary>
public sealed partial class Return : ILInstruction
{
public Return(ILInstruction value) : base(OpCode.Return)
{
this.Value = value;
}
public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true);
ILInstruction value;
public ILInstruction Value {
get { return this.value; }
set {
ValidateChild(value);
SetChildInstruction(ref this.value, value, 0);
}
}
protected sealed override int GetChildCount()
{
return 1;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
case 0:
return this.value;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
case 0:
this.Value = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return ValueSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone()
{
var clone = (Return)ShallowClone();
clone.Value = this.value.Clone();
return clone;
}
public override StackType ResultType { get { return StackType.Void; } }
protected override InstructionFlags ComputeFlags()
{
return value.Flags | InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable;
}
}
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
output.Write('(');
this.value.WriteTo(output);
output.Write(')');
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitReturn(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitReturn(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitReturn(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as Return;
return o != null && this.value.PerformMatch(o.value, ref match);
}
}
}
namespace ICSharpCode.Decompiler.IL
{ {
/// <summary>Load address of instance field</summary> /// <summary>Load address of instance field</summary>
public sealed partial class LdFlda : ILInstruction, IInstructionWithFieldOperand public sealed partial class LdFlda : ILInstruction, IInstructionWithFieldOperand
@ -4319,10 +4272,6 @@ namespace ICSharpCode.Decompiler.IL
{ {
Default(inst); Default(inst);
} }
protected internal virtual void VisitReturn(Return inst)
{
Default(inst);
}
protected internal virtual void VisitLdFlda(LdFlda inst) protected internal virtual void VisitLdFlda(LdFlda inst)
{ {
Default(inst); Default(inst);
@ -4597,10 +4546,6 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(inst); return Default(inst);
} }
protected internal virtual T VisitReturn(Return inst)
{
return Default(inst);
}
protected internal virtual T VisitLdFlda(LdFlda inst) protected internal virtual T VisitLdFlda(LdFlda inst)
{ {
return Default(inst); return Default(inst);
@ -4875,10 +4820,6 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(inst, context); return Default(inst, context);
} }
protected internal virtual T VisitReturn(Return inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitLdFlda(LdFlda inst, C context) protected internal virtual T VisitLdFlda(LdFlda inst, C context)
{ {
return Default(inst, context); return Default(inst, context);
@ -5019,7 +4960,6 @@ namespace ICSharpCode.Decompiler.IL
"ldtypetoken", "ldtypetoken",
"ldmembertoken", "ldmembertoken",
"localloc", "localloc",
"ret",
"ldflda", "ldflda",
"ldsflda", "ldsflda",
"castclass", "castclass",
@ -5299,16 +5239,6 @@ namespace ICSharpCode.Decompiler.IL
argument = default(ILInstruction); argument = default(ILInstruction);
return false; return false;
} }
public bool MatchReturn(out ILInstruction value)
{
var inst = this as Return;
if (inst != null) {
value = inst.Value;
return true;
}
value = default(ILInstruction);
return false;
}
public bool MatchLdFlda(out ILInstruction target, out IField field) public bool MatchLdFlda(out ILInstruction target, out IField field)
{ {
var inst = this as LdFlda; var inst = this as LdFlda;

6
ICSharpCode.Decompiler/IL/Instructions.tt

@ -77,8 +77,8 @@
new OpCode("br", "Unconditional branch. <c>goto target;</c>", new OpCode("br", "Unconditional branch. <c>goto target;</c>",
CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, CustomComputeFlags, CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, CustomComputeFlags,
MatchCondition("this.TargetBlock == o.TargetBlock")), MatchCondition("this.TargetBlock == o.TargetBlock")),
new OpCode("leave", "Unconditional branch to end of block container. <c>goto container_end;</c>, often <c>break;</c>", new OpCode("leave", "Unconditional branch to end of block container. Return is represented using IsLeavingFunction and an (optional) return value. The block container evaluates to the value produced by the argument of the leave instruction.",
NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, CustomComputeFlags, CustomConstructor, CustomArguments("value"), UnconditionalBranch, MayBranch, CustomWriteTo, CustomComputeFlags,
MatchCondition("this.TargetContainer == o.TargetContainer")), MatchCondition("this.TargetContainer == o.TargetContainer")),
new OpCode("if", "If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c>", new OpCode("if", "If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c>",
CustomClassName("IfInstruction"), CustomClassName("IfInstruction"),
@ -164,8 +164,6 @@
CustomClassName("LdMemberToken"), NoArguments, HasMemberOperand, ResultType("O")), CustomClassName("LdMemberToken"), NoArguments, HasMemberOperand, ResultType("O")),
new OpCode("localloc", "Allocates space in the stack frame", new OpCode("localloc", "Allocates space in the stack frame",
CustomClassName("LocAlloc"), Unary, ResultType("I"), MayThrow), CustomClassName("LocAlloc"), Unary, ResultType("I"), MayThrow),
new OpCode("ret", "Returns from the current method or lambda. Only used when returning a value; void returns are represented using a 'leave' instruction.",
CustomClassName("Return"), CustomArguments("value"), MayBranch, UnconditionalBranch),
new OpCode("ldflda", "Load address of instance field", new OpCode("ldflda", "Load address of instance field",
CustomClassName("LdFlda"), CustomArguments("target"), MayThrowIfNotDelayed, HasFieldOperand, ResultType("Ref")), CustomClassName("LdFlda"), CustomArguments("target"), MayThrowIfNotDelayed, HasFieldOperand, ResultType("Ref")),

10
ICSharpCode.Decompiler/IL/Instructions/Leave.cs

@ -30,7 +30,7 @@ namespace ICSharpCode.Decompiler.IL
/// Phase-2 execution removes PopCount elements from the evaluation stack /// Phase-2 execution removes PopCount elements from the evaluation stack
/// and jumps to the target block. /// and jumps to the target block.
/// </remarks> /// </remarks>
partial class Leave : SimpleInstruction partial class Leave : ILInstruction
{ {
BlockContainer targetContainer; BlockContainer targetContainer;
@ -39,11 +39,12 @@ namespace ICSharpCode.Decompiler.IL
// Note: ILReader will create Leave instructions with targetContainer==null to represent 'endfinally', // Note: ILReader will create Leave instructions with targetContainer==null to represent 'endfinally',
// the targetContainer will then be filled in by BlockBuilder // the targetContainer will then be filled in by BlockBuilder
this.targetContainer = targetContainer; this.targetContainer = targetContainer;
this.Value = new Nop();
} }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
return InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable; return value.Flags | InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable;
} }
public override InstructionFlags DirectFlags { public override InstructionFlags DirectFlags {
@ -78,7 +79,7 @@ namespace ICSharpCode.Decompiler.IL
} }
public string TargetLabel { public string TargetLabel {
get { return targetContainer != null ? targetContainer.EntryPoint.Label : string.Empty; } get { return targetContainer?.EntryPoint != null ? targetContainer.EntryPoint.Label : string.Empty; }
} }
/// <summary> /// <summary>
@ -104,6 +105,9 @@ namespace ICSharpCode.Decompiler.IL
if (targetContainer != null) { if (targetContainer != null) {
output.Write(' '); output.Write(' ');
output.WriteReference(TargetLabel, targetContainer, isLocal: true); output.WriteReference(TargetLabel, targetContainer, isLocal: true);
output.Write(" (");
value.WriteTo(output);
output.Write(')');
} }
} }
} }

30
ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs

@ -120,7 +120,18 @@ namespace ICSharpCode.Decompiler.IL
array = null; array = null;
return false; return false;
} }
public bool MatchReturn(out ILInstruction value)
{
var inst = this as Leave;
if (inst != null && inst.IsLeavingFunction) {
value = inst.Value;
return true;
}
value = default(ILInstruction);
return false;
}
public bool MatchBranch(out Block targetBlock) public bool MatchBranch(out Block targetBlock)
{ {
var inst = this as Branch; var inst = this as Branch;
@ -137,11 +148,24 @@ namespace ICSharpCode.Decompiler.IL
var inst = this as Branch; var inst = this as Branch;
return inst != null && inst.TargetBlock == targetBlock; return inst != null && inst.TargetBlock == targetBlock;
} }
public bool MatchLeave(out BlockContainer targetContainer, out ILInstruction value)
{
var inst = this as Leave;
if (inst != null) {
targetContainer = inst.TargetContainer;
value = inst.Value;
return true;
}
targetContainer = null;
value = null;
return false;
}
public bool MatchLeave(out BlockContainer targetContainer) public bool MatchLeave(out BlockContainer targetContainer)
{ {
var inst = this as Leave; var inst = this as Leave;
if (inst != null) { if (inst != null && inst.Value.MatchNop()) {
targetContainer = inst.TargetContainer; targetContainer = inst.TargetContainer;
return true; return true;
} }
@ -152,7 +176,7 @@ namespace ICSharpCode.Decompiler.IL
public bool MatchLeave(BlockContainer targetContainer) public bool MatchLeave(BlockContainer targetContainer)
{ {
var inst = this as Leave; var inst = this as Leave;
return inst != null && inst.TargetContainer == targetContainer; return inst != null && inst.TargetContainer == targetContainer && inst.Value.MatchNop();
} }
public bool MatchIfInstruction(out ILInstruction condition, out ILInstruction trueInst, out ILInstruction falseInst) public bool MatchIfInstruction(out ILInstruction condition, out ILInstruction trueInst, out ILInstruction falseInst)

2
ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs

@ -259,7 +259,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (p != null && !string.IsNullOrEmpty(p.Name)) if (p != null && !string.IsNullOrEmpty(p.Name))
return CleanUpVariableName(p.Name); return CleanUpVariableName(p.Name);
break; break;
case Return ret: case Leave ret:
return "result"; return "result";
} }
return null; return null;

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

@ -272,7 +272,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// decide based on the target into which we are inlining // decide based on the target into which we are inlining
var parent = loadInst.Parent; var parent = loadInst.Parent;
switch (next.OpCode) { switch (next.OpCode) {
case OpCode.Return: case OpCode.Leave:
return parent == next; return parent == next;
case OpCode.IfInstruction: case OpCode.IfInstruction:
while (parent.OpCode == OpCode.LogicNot) { while (parent.OpCode == OpCode.LogicNot) {

Loading…
Cancel
Save