Browse Source

Add InvalidInstruction to represent invalid IL opcodes in the ILAst instead of crashing.

pull/728/head
Daniel Grunwald 10 years ago
parent
commit
a40f0754c1
  1. 14
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 8
      ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs
  3. 18
      ICSharpCode.Decompiler/IL/ILReader.cs
  4. 45
      ICSharpCode.Decompiler/IL/Instructions.cs
  5. 2
      ICSharpCode.Decompiler/IL/Instructions.tt
  6. 6
      ICSharpCode.Decompiler/IL/Instructions/Branch.cs
  7. 6
      ICSharpCode.Decompiler/IL/Instructions/Leave.cs
  8. 20
      ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs

14
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -953,11 +953,23 @@ namespace ICSharpCode.Decompiler.CSharp
return true; return true;
} }
protected internal override TranslatedExpression VisitInvalidInstruction(InvalidInstruction inst)
{
string message = "Invalid IL";
if (inst.ILRange.Start != 0) {
message += $" near IL_{inst.ILRange.Start:x4}";
}
if (!string.IsNullOrEmpty(inst.Message)) {
message += ": " + inst.Message;
}
return ErrorExpression(message);
}
protected override TranslatedExpression Default(ILInstruction inst) protected override TranslatedExpression Default(ILInstruction inst)
{ {
return ErrorExpression("OpCode not supported: " + inst.OpCode); return ErrorExpression("OpCode not supported: " + inst.OpCode);
} }
static TranslatedExpression ErrorExpression(string message) static TranslatedExpression ErrorExpression(string message)
{ {
var e = new ErrorExpression(); var e = new ErrorExpression();

8
ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs

@ -278,7 +278,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
DebugStartPoint(inst); DebugStartPoint(inst);
// This method assumes normal control flow and no branches. // This method assumes normal control flow and no branches.
if ((inst.DirectFlags & (InstructionFlags.ControlFlow | InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable)) != 0) { if ((inst.DirectFlags & (InstructionFlags.ControlFlow | InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable)) != 0) {
throw new NotImplementedException("RDVisitor is missing implementation for " + inst.GetType().Name); throw new NotImplementedException(GetType().Name + " is missing implementation for " + inst.GetType().Name);
} }
// Since this instruction has normal control flow, we can evaluate our children left-to-right. // Since this instruction has normal control flow, we can evaluate our children left-to-right.
@ -438,7 +438,11 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
protected internal override void VisitRethrow(Rethrow inst) protected internal override void VisitRethrow(Rethrow inst)
{ {
PropagateStateOnException(); MarkUnreachable();
}
protected internal override void VisitInvalidInstruction(InvalidInstruction inst)
{
MarkUnreachable(); MarkUnreachable();
} }

18
ICSharpCode.Decompiler/IL/ILReader.cs

@ -242,7 +242,7 @@ namespace ICSharpCode.Decompiler.IL
decodedInstruction.ILRange = new Interval(start, reader.Position); decodedInstruction.ILRange = new Interval(start, reader.Position);
UnpackPush(decodedInstruction).ILRange = decodedInstruction.ILRange; UnpackPush(decodedInstruction).ILRange = decodedInstruction.ILRange;
instructionBuilder.Add(decodedInstruction); instructionBuilder.Add(decodedInstruction);
if (decodedInstruction.HasFlag(InstructionFlags.EndPointUnreachable)) { if ((decodedInstruction.DirectFlags & InstructionFlags.EndPointUnreachable) != 0) {
if (!stackByOffset.TryGetValue(reader.Position, out currentStack)) { if (!stackByOffset.TryGetValue(reader.Position, out currentStack)) {
currentStack = ImmutableStack<ILVariable>.Empty; currentStack = ImmutableStack<ILVariable>.Empty;
} }
@ -763,7 +763,7 @@ namespace ICSharpCode.Decompiler.IL
case ILOpCode.Unbox_Any: case ILOpCode.Unbox_Any:
return Push(new UnboxAny(Pop(), ReadAndDecodeTypeReference())); return Push(new UnboxAny(Pop(), ReadAndDecodeTypeReference()));
default: default:
throw new NotImplementedException(ilOpCode.ToString()); return new InvalidInstruction("Unknown opcode: " + ilOpCode.ToString());
} }
} }
@ -832,17 +832,19 @@ namespace ICSharpCode.Decompiler.IL
return new StLoc(v, inst); return new StLoc(v, inst);
} }
LdLoc Peek() ILInstruction Peek()
{ {
Debug.Assert(!currentStack.IsEmpty); if (currentStack.IsEmpty) {
// TODO: handle stack underflow? return new InvalidInstruction("Stack underflow") { ILRange = new Interval(reader.Position, reader.Position) };
}
return new LdLoc(currentStack.Peek()); return new LdLoc(currentStack.Peek());
} }
LdLoc Pop() ILInstruction Pop()
{ {
Debug.Assert(!currentStack.IsEmpty); if (currentStack.IsEmpty) {
// TODO: handle stack underflow? return new InvalidInstruction("Stack underflow") { ILRange = new Interval(reader.Position, reader.Position) };
}
ILVariable v; ILVariable v;
currentStack = currentStack.Pop(out v); currentStack = currentStack.Pop(out v);
return new LdLoc(v); return new LdLoc(v);

45
ICSharpCode.Decompiler/IL/Instructions.cs

@ -29,6 +29,8 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
public enum OpCode public enum OpCode
{ {
/// <summary>Represents invalid IL. Semantically, this instruction is considered to throw some kind of exception.</summary>
InvalidInstruction,
/// <summary>No operation. Takes 0 arguments and returns void.</summary> /// <summary>No operation. Takes 0 arguments and returns void.</summary>
Nop, Nop,
/// <summary>A container of IL blocks.</summary> /// <summary>A container of IL blocks.</summary>
@ -440,6 +442,32 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
/// <summary>Represents invalid IL. Semantically, this instruction is considered to throw some kind of exception.</summary>
public sealed partial class InvalidInstruction : SimpleInstruction
{
public InvalidInstruction() : base(OpCode.InvalidInstruction)
{
}
public override StackType ResultType { get { return StackType.Void; } }
protected override InstructionFlags ComputeFlags()
{
return InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable;
}
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitInvalidInstruction(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitInvalidInstruction(this);
}
}
/// <summary>No operation. Takes 0 arguments and returns void.</summary> /// <summary>No operation. Takes 0 arguments and returns void.</summary>
public sealed partial class Nop : SimpleInstruction public sealed partial class Nop : SimpleInstruction
{ {
@ -3068,6 +3096,10 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Called by Visit*() methods that were not overridden</summary> /// <summary>Called by Visit*() methods that were not overridden</summary>
protected abstract void Default(ILInstruction inst); protected abstract void Default(ILInstruction inst);
protected internal virtual void VisitInvalidInstruction(InvalidInstruction inst)
{
Default(inst);
}
protected internal virtual void VisitNop(Nop inst) protected internal virtual void VisitNop(Nop inst)
{ {
Default(inst); Default(inst);
@ -3366,6 +3398,10 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Called by Visit*() methods that were not overridden</summary> /// <summary>Called by Visit*() methods that were not overridden</summary>
protected abstract T Default(ILInstruction inst); protected abstract T Default(ILInstruction inst);
protected internal virtual T VisitInvalidInstruction(InvalidInstruction inst)
{
return Default(inst);
}
protected internal virtual T VisitNop(Nop inst) protected internal virtual T VisitNop(Nop inst)
{ {
return Default(inst); return Default(inst);
@ -3701,6 +3737,7 @@ namespace ICSharpCode.Decompiler.IL
partial class InstructionOutputExtensions partial class InstructionOutputExtensions
{ {
static readonly string[] originalOpCodeNames = { static readonly string[] originalOpCodeNames = {
"invalid",
"nop", "nop",
"ILFunction", "ILFunction",
"BlockContainer", "BlockContainer",
@ -3778,6 +3815,14 @@ namespace ICSharpCode.Decompiler.IL
partial class ILInstruction partial class ILInstruction
{ {
public bool MatchInvalidInstruction()
{
var inst = this as InvalidInstruction;
if (inst != null) {
return true;
}
return false;
}
public bool MatchNop() public bool MatchNop()
{ {
var inst = this as Nop; var inst = this as Nop;

2
ICSharpCode.Decompiler/IL/Instructions.tt

@ -36,6 +36,8 @@
}; };
OpCode[] opCodes = { OpCode[] opCodes = {
new OpCode("invalid", "Represents invalid IL. Semantically, this instruction is considered to throw some kind of exception.",
CustomClassName("InvalidInstruction"), NoArguments, MayThrow, UnconditionalBranch),
new OpCode("nop", "No operation. Takes 0 arguments and returns void.", new OpCode("nop", "No operation. Takes 0 arguments and returns void.",
VoidResult, NoArguments), VoidResult, NoArguments),
new OpCode("ILFunction", "A container of IL blocks.", new OpCode("ILFunction", "A container of IL blocks.",

6
ICSharpCode.Decompiler/IL/Instructions/Branch.cs

@ -55,6 +55,12 @@ namespace ICSharpCode.Decompiler.IL
return InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable; return InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable;
}
}
public int TargetILOffset { public int TargetILOffset {
get { return targetBlock != null ? targetBlock.ILRange.Start : targetILOffset; } get { return targetBlock != null ? targetBlock.ILRange.Start : targetILOffset; }
} }

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

@ -52,6 +52,12 @@ namespace ICSharpCode.Decompiler.IL
return InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable; return InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable;
} }
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable;
}
}
public BlockContainer TargetContainer { public BlockContainer TargetContainer {
get { return targetContainer; } get { return targetContainer; }
set { set {

20
ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs

@ -36,4 +36,24 @@ namespace ICSharpCode.Decompiler.IL
// the non-custom WriteTo would add useless parentheses // the non-custom WriteTo would add useless parentheses
} }
} }
partial class InvalidInstruction : SimpleInstruction
{
public string Message;
public InvalidInstruction(string message) : this()
{
this.Message = message;
}
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
if (!string.IsNullOrEmpty(Message)) {
output.Write('(');
output.Write(Message);
output.Write(')');
}
}
}
} }

Loading…
Cancel
Save