Browse Source

Add result type invariants for instruction arguments.

pull/1278/head
Siegfried Pammer 8 years ago
parent
commit
75a3f6d24b
  1. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  2. 25
      ICSharpCode.Decompiler/IL/ILReader.cs
  3. 46
      ICSharpCode.Decompiler/IL/Instructions.cs
  4. 46
      ICSharpCode.Decompiler/IL/Instructions.tt
  5. 31
      ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs
  6. 4
      ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
  7. 1
      ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -285,6 +285,7 @@
<Compile Include="DotNetCore\UniversalAssemblyResolver.cs" /> <Compile Include="DotNetCore\UniversalAssemblyResolver.cs" />
<Compile Include="DotNetCore\UnresolvedAssemblyNameReference.cs" /> <Compile Include="DotNetCore\UnresolvedAssemblyNameReference.cs" />
<Compile Include="IL\ILAstWritingOptions.cs" /> <Compile Include="IL\ILAstWritingOptions.cs" />
<Compile Include="IL\Instructions\LdFlda.cs" />
<Compile Include="IL\Instructions\StLoc.cs" /> <Compile Include="IL\Instructions\StLoc.cs" />
<Compile Include="IL\SequencePoint.cs" /> <Compile Include="IL\SequencePoint.cs" />
<Compile Include="IL\Instructions\CallIndirect.cs" /> <Compile Include="IL\Instructions\CallIndirect.cs" />

25
ICSharpCode.Decompiler/IL/ILReader.cs

@ -861,14 +861,17 @@ namespace ICSharpCode.Decompiler.IL
case Cil.Code.Ldfld: case Cil.Code.Ldfld:
{ {
var field = ReadAndDecodeFieldReference(); var field = ReadAndDecodeFieldReference();
return Push(new LdObj(new LdFlda(Pop(), field) { DelayExceptions = true }, field.Type)); return Push(new LdObj(new LdFlda(PopLdFldTarget(field), field) { DelayExceptions = true }, field.Type));
} }
case Cil.Code.Ldflda: case Cil.Code.Ldflda:
return Push(new LdFlda(Pop(), ReadAndDecodeFieldReference())); {
var field = ReadAndDecodeFieldReference();
return Push(new LdFlda(PopFieldTarget(field), field));
}
case Cil.Code.Stfld: case Cil.Code.Stfld:
{ {
var field = ReadAndDecodeFieldReference(); var field = ReadAndDecodeFieldReference();
return new StObj(value: Pop(field.Type.GetStackType()), target: new LdFlda(Pop(), field) { DelayExceptions = true }, type: field.Type); return new StObj(value: Pop(field.Type.GetStackType()), target: new LdFlda(PopFieldTarget(field), field) { DelayExceptions = true }, type: field.Type);
} }
case Cil.Code.Ldlen: case Cil.Code.Ldlen:
return Push(new LdLen(StackType.I, Pop())); return Push(new LdLen(StackType.I, Pop()));
@ -935,7 +938,6 @@ namespace ICSharpCode.Decompiler.IL
return new InvalidBranch("Unknown opcode: " + cecilInst.OpCode.ToString()); return new InvalidBranch("Unknown opcode: " + cecilInst.OpCode.ToString());
} }
} }
StackType PeekStackType() StackType PeekStackType()
{ {
@ -1078,7 +1080,20 @@ namespace ICSharpCode.Decompiler.IL
return inst; return inst;
} }
} }
ILInstruction PopFieldTarget(IField field)
{
return field.DeclaringType.IsReferenceType == true ? Pop(StackType.O) : PopPointer();
}
ILInstruction PopLdFldTarget(IField field)
{
if (field.DeclaringType.IsReferenceType == true)
return Pop(StackType.O);
return PeekStackType() == StackType.O ? new AddressOf(Pop()) : PopPointer();
}
private ILInstruction Return() private ILInstruction Return()
{ {
if (methodReturnStackType == StackType.Void) if (methodReturnStackType == StackType.Void)

46
ICSharpCode.Decompiler/IL/Instructions.cs

@ -1725,6 +1725,11 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LockInstruction; var o = other as LockInstruction;
return o != null && this.onExpression.PerformMatch(o.onExpression, ref match) && this.body.PerformMatch(o.body, ref match); return o != null && this.onExpression.PerformMatch(o.onExpression, ref match) && this.body.PerformMatch(o.body, ref match);
} }
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(onExpression.ResultType == StackType.O);
}
} }
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
@ -1867,6 +1872,7 @@ namespace ICSharpCode.Decompiler.IL
base.CheckInvariant(phase); base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function)); Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function));
Debug.Assert(phase <= ILPhase.InILReader || variable.Function.Variables[variable.IndexInFunction] == variable); Debug.Assert(phase <= ILPhase.InILReader || variable.Function.Variables[variable.IndexInFunction] == variable);
Debug.Assert(resourceExpression.ResultType == StackType.O);
} }
} }
} }
@ -3100,6 +3106,13 @@ namespace ICSharpCode.Decompiler.IL
var o = other as Cpblk; var o = other as Cpblk;
return o != null && this.destAddress.PerformMatch(o.destAddress, ref match) && this.sourceAddress.PerformMatch(o.sourceAddress, ref match) && this.size.PerformMatch(o.size, ref match) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix; return o != null && this.destAddress.PerformMatch(o.destAddress, ref match) && this.sourceAddress.PerformMatch(o.sourceAddress, ref match) && this.size.PerformMatch(o.size, ref match) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
} }
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(destAddress.ResultType == StackType.I || destAddress.ResultType == StackType.Ref);
Debug.Assert(sourceAddress.ResultType == StackType.I || sourceAddress.ResultType == StackType.Ref);
Debug.Assert(size.ResultType == StackType.I4);
}
} }
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
@ -3241,6 +3254,13 @@ namespace ICSharpCode.Decompiler.IL
var o = other as Initblk; var o = other as Initblk;
return o != null && this.address.PerformMatch(o.address, ref match) && this.value.PerformMatch(o.value, ref match) && this.size.PerformMatch(o.size, ref match) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix; return o != null && this.address.PerformMatch(o.address, ref match) && this.value.PerformMatch(o.value, ref match) && this.size.PerformMatch(o.size, ref match) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
} }
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(address.ResultType == StackType.I || address.ResultType == StackType.Ref);
Debug.Assert(value.ResultType == StackType.I4);
Debug.Assert(size.ResultType == StackType.I4);
}
} }
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
@ -3589,6 +3609,11 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LdObj; var o = other as LdObj;
return o != null && this.target.PerformMatch(o.target, ref match) && type.Equals(o.type) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix; return o != null && this.target.PerformMatch(o.target, ref match) && type.Equals(o.type) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
} }
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(target.ResultType == StackType.Ref || target.ResultType == StackType.I);
}
} }
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
@ -3720,6 +3745,12 @@ namespace ICSharpCode.Decompiler.IL
var o = other as StObj; var o = other as StObj;
return o != null && this.target.PerformMatch(o.target, ref match) && this.value.PerformMatch(o.value, ref match) && type.Equals(o.type) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix; return o != null && this.target.PerformMatch(o.target, ref match) && this.value.PerformMatch(o.value, ref match) && type.Equals(o.type) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
} }
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(target.ResultType == StackType.Ref || target.ResultType == StackType.I);
Debug.Assert(value.ResultType == type.GetStackType());
}
} }
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
@ -4238,6 +4269,11 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LdLen; var o = other as LdLen;
return o != null && this.array.PerformMatch(o.array, ref match); return o != null && this.array.PerformMatch(o.array, ref match);
} }
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(array.ResultType == StackType.O);
}
} }
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
@ -4450,6 +4486,11 @@ namespace ICSharpCode.Decompiler.IL
var o = other as ArrayToPointer; var o = other as ArrayToPointer;
return o != null && this.array.PerformMatch(o.array, ref match); return o != null && this.array.PerformMatch(o.array, ref match);
} }
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(array.ResultType == StackType.O);
}
} }
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
@ -4531,6 +4572,11 @@ namespace ICSharpCode.Decompiler.IL
var o = other as StringToInt; var o = other as StringToInt;
return o != null && this.argument.PerformMatch(o.argument, ref match); return o != null && this.argument.PerformMatch(o.argument, ref match);
} }
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(argument.ResultType == StackType.O);
}
} }
} }
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL

46
ICSharpCode.Decompiler/IL/Instructions.tt

@ -27,9 +27,9 @@
new OpCode("SimpleInstruction", "Instruction without any arguments", new OpCode("SimpleInstruction", "Instruction without any arguments",
AbstractBaseClass, CustomArguments(), CustomWriteTo, HasFlag("InstructionFlags.None")), AbstractBaseClass, CustomArguments(), CustomWriteTo, HasFlag("InstructionFlags.None")),
new OpCode("UnaryInstruction", "Instruction with a single argument", new OpCode("UnaryInstruction", "Instruction with a single argument",
AbstractBaseClass, CustomArguments("argument"), HasFlag("InstructionFlags.None")), AbstractBaseClass, CustomArguments(("argument", null)), HasFlag("InstructionFlags.None")),
new OpCode("BinaryInstruction", "Instruction with two arguments: Left and Right", new OpCode("BinaryInstruction", "Instruction with two arguments: Left and Right",
AbstractBaseClass, CustomArguments("left", "right"), HasFlag("InstructionFlags.None")), AbstractBaseClass, CustomArguments(("left", null), ("right", null)), HasFlag("InstructionFlags.None")),
new OpCode("CallInstruction", "Instruction with a list of arguments.", new OpCode("CallInstruction", "Instruction with a list of arguments.",
AbstractBaseClass, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}), AbstractBaseClass, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}),
CustomWriteTo, MayThrow, SideEffect), CustomWriteTo, MayThrow, SideEffect),
@ -68,7 +68,7 @@
MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && IsLifted == o.IsLifted")), MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && IsLifted == o.IsLifted")),
new OpCode("compound", "Common instruction for compound assignments.", new OpCode("compound", "Common instruction for compound assignments.",
CustomClassName("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags, CustomClassName("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags,
MayThrow, CustomArguments("target", "value"), HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo, MayThrow, CustomArguments(("target", null), ("value", null)), HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo,
MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator")), MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator")),
new OpCode("bit.not", "Bitwise NOT", Unary, CustomConstructor, MatchCondition("IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType")), new OpCode("bit.not", "Bitwise NOT", Unary, CustomConstructor, MatchCondition("IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType")),
new OpCode("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("O")), new OpCode("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("O")),
@ -76,7 +76,7 @@
CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch,
MatchCondition("this.TargetBlock == o.TargetBlock")), MatchCondition("this.TargetBlock == o.TargetBlock")),
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.", 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.",
CustomConstructor, CustomArguments("value"), UnconditionalBranch, MayBranch, CustomWriteTo, CustomComputeFlags, CustomConstructor, CustomArguments(("value", null)), 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"),
@ -116,12 +116,12 @@
MatchCondition("faultBlock.PerformMatch(o.faultBlock, ref match)")), MatchCondition("faultBlock.PerformMatch(o.faultBlock, ref match)")),
new OpCode("lock", "Lock statement", CustomClassName("LockInstruction"), new OpCode("lock", "Lock statement", CustomClassName("LockInstruction"),
CustomChildren(new [] { CustomChildren(new [] {
new ArgumentInfo("onExpression"), new ArgumentInfo("onExpression") { ExpectedTypes = new[] { "O" }},
new ChildInfo("body") new ChildInfo("body")
}), CustomWriteTo, ControlFlow, SideEffect, ResultType("Void")), }), CustomWriteTo, ControlFlow, SideEffect, ResultType("Void")),
new OpCode("using", "Using statement", CustomClassName("UsingInstruction"), HasVariableOperand("Store"), new OpCode("using", "Using statement", CustomClassName("UsingInstruction"), HasVariableOperand("Store"),
CustomChildren(new [] { CustomChildren(new [] {
new ArgumentInfo("resourceExpression"), new ArgumentInfo("resourceExpression") { ExpectedTypes = new[] { "O" }},
new ChildInfo("body") new ChildInfo("body")
}), CustomWriteTo, ControlFlow, SideEffect, ResultType("Void")), }), CustomWriteTo, ControlFlow, SideEffect, ResultType("Void")),
new OpCode("debug.break", "Breakpoint instruction", new OpCode("debug.break", "Breakpoint instruction",
@ -152,10 +152,10 @@
CustomClassName("LdLoca"), NoArguments, ResultType("Ref"), HasVariableOperand("Address")), CustomClassName("LdLoca"), NoArguments, ResultType("Ref"), HasVariableOperand("Address")),
new OpCode("stloc", "Stores a value into a local variable. (IL: starg/stloc)" + Environment.NewLine new OpCode("stloc", "Stores a value into a local variable. (IL: starg/stloc)" + Environment.NewLine
+ "Evaluates to the value that was stored (for byte/short variables: evaluates to the truncated value, sign/zero extended back to I4 based on variable.Type.GetSign())", + "Evaluates to the value that was stored (for byte/short variables: evaluates to the truncated value, sign/zero extended back to I4 based on variable.Type.GetSign())",
CustomClassName("StLoc"), HasVariableOperand("Store", generateCheckInvariant: false), CustomArguments("value"), CustomClassName("StLoc"), HasVariableOperand("Store", generateCheckInvariant: false), CustomArguments(("value", null)),
ResultType("variable.StackType")), ResultType("variable.StackType")),
new OpCode("addressof", "Stores the value into an anonymous temporary variable, and returns the address of that variable.", new OpCode("addressof", "Stores the value into an anonymous temporary variable, and returns the address of that variable.",
CustomClassName("AddressOf"), CustomArguments("value"), ResultType("Ref")), CustomClassName("AddressOf"), CustomArguments(("value", null)), ResultType("Ref")),
new OpCode("3vl.logic.and", "Three valued logic and. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.and(), does not have short-circuiting behavior.", new OpCode("3vl.logic.and", "Three valued logic and. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.and(), does not have short-circuiting behavior.",
CustomClassName("ThreeValuedLogicAnd"), Binary, ResultType("O")), CustomClassName("ThreeValuedLogicAnd"), Binary, ResultType("O")),
new OpCode("3vl.logic.or", "Three valued logic or. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.or(), does not have short-circuiting behavior.", new OpCode("3vl.logic.or", "Three valued logic or. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.or(), does not have short-circuiting behavior.",
@ -186,16 +186,16 @@
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("cpblk", "memcpy(destAddress, sourceAddress, size);", new OpCode("cpblk", "memcpy(destAddress, sourceAddress, size);",
CustomArguments("destAddress", "sourceAddress", "size"), CustomArguments(("destAddress", new[] { "I", "Ref" }), ("sourceAddress", new[] { "I", "Ref" }), ("size", new[] { "I4" })),
MayThrow, MemoryAccess, MayThrow, MemoryAccess,
SupportsVolatilePrefix, SupportsUnalignedPrefix, ResultType("Void")), SupportsVolatilePrefix, SupportsUnalignedPrefix, ResultType("Void")),
new OpCode("initblk", "memset(address, value, size)", new OpCode("initblk", "memset(address, value, size)",
CustomArguments("address", "value", "size"), CustomArguments(("address", new[] { "I", "Ref" }), ("value", new[] { "I4" }), ("size", new[] { "I4" })),
MayThrow, MemoryAccess, MayThrow, MemoryAccess,
SupportsVolatilePrefix, SupportsUnalignedPrefix, ResultType("Void")), SupportsVolatilePrefix, SupportsUnalignedPrefix, ResultType("Void")),
new OpCode("ldflda", "Load address of instance field", new OpCode("ldflda", "Load address of instance field",
CustomClassName("LdFlda"), CustomArguments("target"), MayThrowIfNotDelayed, HasFieldOperand, CustomClassName("LdFlda"), CustomArguments(("target", null)), MayThrowIfNotDelayed, HasFieldOperand,
ResultType("target.ResultType.IsIntegerType() ? StackType.I : StackType.Ref")), ResultType("target.ResultType.IsIntegerType() ? StackType.I : StackType.Ref")),
new OpCode("ldsflda", "Load static field address", new OpCode("ldsflda", "Load static field address",
CustomClassName("LdsFlda"), NoArguments, ResultType("Ref"), HasFieldOperand), CustomClassName("LdsFlda"), NoArguments, ResultType("Ref"), HasFieldOperand),
@ -205,11 +205,11 @@
new OpCode("isinst", "Test if object is instance of class or interface.", new OpCode("isinst", "Test if object is instance of class or interface.",
CustomClassName("IsInst"), Unary, HasTypeOperand, ResultType("O")), CustomClassName("IsInst"), Unary, HasTypeOperand, ResultType("O")),
new OpCode("ldobj", "Indirect load (ref/pointer dereference).", new OpCode("ldobj", "Indirect load (ref/pointer dereference).",
CustomClassName("LdObj"), CustomArguments("target"), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal, CustomClassName("LdObj"), CustomArguments(("target", new[] { "Ref", "I" })), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal,
SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")), SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")),
new OpCode("stobj", "Indirect store (store to ref/pointer)." + Environment.NewLine new OpCode("stobj", "Indirect store (store to ref/pointer)." + Environment.NewLine
+ "Evaluates to the value that was stored (when using type byte/short: evaluates to the truncated value, sign/zero extended back to I4 based on type.GetSign())", + "Evaluates to the value that was stored (when using type byte/short: evaluates to the truncated value, sign/zero extended back to I4 based on type.GetSign())",
CustomClassName("StObj"), CustomArguments("target", "value"), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal, CustomClassName("StObj"), CustomArguments(("target", new[] { "Ref", "I" }), ("value", new[] { "type.GetStackType()" })), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal,
SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")), SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")),
new OpCode("box", "Boxes a value.", new OpCode("box", "Boxes a value.",
@ -232,15 +232,15 @@
CustomClassName("SizeOf"), NoArguments, HasTypeOperand, ResultType("I4")), CustomClassName("SizeOf"), NoArguments, HasTypeOperand, ResultType("I4")),
new OpCode("ldlen", "Returns the length of an array as 'native unsigned int'.", new OpCode("ldlen", "Returns the length of an array as 'native unsigned int'.",
CustomClassName("LdLen"), CustomArguments("array"), CustomConstructor, CustomWriteTo, MayThrow), CustomClassName("LdLen"), CustomArguments(("array", new[] { "O" })), CustomConstructor, CustomWriteTo, MayThrow),
new OpCode("ldelema", "Load address of array element.", new OpCode("ldelema", "Load address of array element.",
CustomClassName("LdElema"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true), CustomClassName("LdElema"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true),
MayThrowIfNotDelayed, ResultType("Ref"), SupportsReadonlyPrefix), MayThrowIfNotDelayed, ResultType("Ref"), SupportsReadonlyPrefix),
new OpCode("array.to.pointer", "Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty." + Environment.NewLine new OpCode("array.to.pointer", "Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty." + Environment.NewLine
+ "Also used to convert a string to a reference to the first character.", + "Also used to convert a string to a reference to the first character.",
CustomArguments("array"), ResultType("Ref")), CustomArguments(("array", new[] { "O" })), ResultType("Ref")),
new OpCode("string.to.int", "Maps a string value to an integer. This is used in switch(string).", new OpCode("string.to.int", "Maps a string value to an integer. This is used in switch(string).",
CustomArguments("argument"), CustomConstructor, CustomWriteTo, ResultType("I4")), CustomArguments(("argument", new[] { "O" })), CustomConstructor, CustomWriteTo, ResultType("I4")),
new OpCode("expression.tree.cast", "ILAst representation of Expression.Convert.", new OpCode("expression.tree.cast", "ILAst representation of Expression.Convert.",
CustomClassName("ExpressionTreeCast"), Unary, HasTypeOperand, MayThrow, CustomConstructor, CustomWriteTo, ResultType("type.GetStackType()"), CustomClassName("ExpressionTreeCast"), Unary, HasTypeOperand, MayThrow, CustomConstructor, CustomWriteTo, ResultType("type.GetStackType()"),
@ -256,11 +256,11 @@
new OpCode("yield.return", "Yield an element from an iterator.", new OpCode("yield.return", "Yield an element from an iterator.",
MayBranch, // yield return may end up returning if the consumer disposes the iterator MayBranch, // yield return may end up returning if the consumer disposes the iterator
SideEffect, // consumer can have arbitrary side effects while we're yielding SideEffect, // consumer can have arbitrary side effects while we're yielding
CustomArguments("value"), VoidResult), CustomArguments(("value", null)), VoidResult),
// note: "yield break" is always represented using a "leave" instruction // note: "yield break" is always represented using a "leave" instruction
new OpCode("await", "C# await operator.", new OpCode("await", "C# await operator.",
SideEffect, // other code can run with arbitrary side effects while we're waiting SideEffect, // other code can run with arbitrary side effects while we're waiting
CustomArguments("value"), ResultType("GetResultMethod?.ReturnType.GetStackType() ?? StackType.Unknown")), CustomArguments(("value", null)), ResultType("GetResultMethod?.ReturnType.GetStackType() ?? StackType.Unknown")),
// patterns // patterns
new OpCode("AnyNode", "Matches any node", Pattern, CustomArguments(), CustomConstructor), new OpCode("AnyNode", "Matches any node", Pattern, CustomArguments(), CustomConstructor),
@ -677,9 +677,9 @@ namespace ICSharpCode.Decompiler.IL
opCode.WriteArguments.Add("output.Write(')');"); opCode.WriteArguments.Add("output.Write(')');");
}; };
static Action<OpCode> CustomArguments(params string[] arguments) static Action<OpCode> CustomArguments(params (string name, string[] expectedTypes)[] arguments)
{ {
return CustomChildren(arguments.Select(arg => new ArgumentInfo(arg)).ToArray(), generateInline: true); return CustomChildren(arguments.Select(arg => new ArgumentInfo(arg.name) { ExpectedTypes = arg.expectedTypes }).ToArray(), generateInline: true);
} }
class ChildInfo class ChildInfo
@ -768,12 +768,16 @@ namespace ICSharpCode.Decompiler.IL
+ "}"); + "}");
} }
if (children[i].ExpectedTypes?.Length > 0) { if (children[i].ExpectedTypes?.Length > 0) {
string checkString = null;
foreach (var expectedType in children[i].ExpectedTypes) { foreach (var expectedType in children[i].ExpectedTypes) {
var expectedTypeCode = expectedType; var expectedTypeCode = expectedType;
if (!expectedType.Contains(".")) if (!expectedType.Contains("."))
expectedTypeCode = "StackType." + expectedTypeCode; expectedTypeCode = "StackType." + expectedTypeCode;
opCode.Invariants.Add("Debug.Assert(" + arg + ".ResultType == " + expectedTypeCode + ");"); if (checkString != null)
checkString += " || ";
checkString += arg + ".ResultType == " + expectedTypeCode;
} }
opCode.Invariants.Add("Debug.Assert(" + checkString + ");");
} }
} }
opCode.WriteArguments.Add("output.Write(')');"); opCode.WriteArguments.Add("output.Write(')');");

31
ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs

@ -0,0 +1,31 @@
// Copyright (c) 2014 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.Diagnostics;
namespace ICSharpCode.Decompiler.IL
{
public sealed partial class LdFlda
{
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(field.DeclaringType.IsReferenceType == true ? (target.ResultType == StackType.O) : (target.ResultType == StackType.I || target.ResultType == StackType.Ref));
}
}
}

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

@ -386,8 +386,10 @@ namespace ICSharpCode.Decompiler.IL
public bool MatchLdFld(out ILInstruction target, out IField field) public bool MatchLdFld(out ILInstruction target, out IField field)
{ {
if (this is LdObj ldobj && ldobj.Target is LdFlda ldflda && ldobj.UnalignedPrefix == 0 && !ldobj.IsVolatile) { if (this is LdObj ldobj && ldobj.Target is LdFlda ldflda && ldobj.UnalignedPrefix == 0 && !ldobj.IsVolatile) {
target = ldflda.Target;
field = ldflda.Field; field = ldflda.Field;
if (field.DeclaringType.IsReferenceType == true || !ldflda.Target.MatchAddressOf(out target)) {
target = ldflda.Target;
}
return true; return true;
} }
target = null; target = null;

1
ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs

@ -147,6 +147,7 @@ namespace ICSharpCode.Decompiler.IL
} }
Debug.Assert(sets.SetEquals(LongSet.Universe), "switch does not handle all possible cases"); Debug.Assert(sets.SetEquals(LongSet.Universe), "switch does not handle all possible cases");
Debug.Assert(!expectNullSection, "Lifted switch is missing 'case null'"); Debug.Assert(!expectNullSection, "Lifted switch is missing 'case null'");
Debug.Assert(this.IsLifted ? (value.ResultType == StackType.O) : (value.ResultType == StackType.I4 || value.ResultType == StackType.I8));
} }
} }

Loading…
Cancel
Save