Browse Source

Initial support for decompiling fixed statements

pull/734/head
Daniel Grunwald 9 years ago
parent
commit
20aec66815
  1. 11
      ICSharpCode.Decompiler/CSharp/Annotations.cs
  2. 4
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  3. 7
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 15
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  5. 14
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  6. 8
      ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs
  7. 12
      ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs
  8. 14
      ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitionsVisitor.cs
  9. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  10. 219
      ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs
  11. 4
      ICSharpCode.Decompiler/IL/ILReader.cs
  12. 205
      ICSharpCode.Decompiler/IL/Instructions.cs
  13. 79
      ICSharpCode.Decompiler/IL/Instructions.tt
  14. 7
      ICSharpCode.Decompiler/IL/Instructions/Branch.cs
  15. 9
      ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
  16. 128
      ICSharpCode.Decompiler/IL/Instructions/LocalVarInstructions.cs
  17. 13
      ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
  18. 32
      ICSharpCode.Decompiler/IL/Instructions/PinnedRegion.cs
  19. 2
      ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs
  20. 32
      ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs
  21. 9
      ICSharpCode.Decompiler/IL/SemanticHelper.cs
  22. 5
      ICSharpCode.Decompiler/Tests/TestCases/UnsafeCode.cs

11
ICSharpCode.Decompiler/CSharp/Annotations.cs

@ -125,6 +125,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -125,6 +125,17 @@ namespace ICSharpCode.Decompiler.CSharp
else
return null;
}
public static ILVariable GetILVariable(this VariableInitializer vi)
{
return vi.Annotation<ILVariable>();
}
public static VariableInitializer WithILVariable(this VariableInitializer vi, ILVariable v)
{
vi.AddAnnotation(v);
return vi;
}
}
public class ILVariableResolveResult : ResolveResult

4
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -54,7 +54,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -54,7 +54,7 @@ namespace ICSharpCode.Decompiler.CSharp
new SplitVariables(),
new ControlFlowSimplification(),
new ILInlining(),
new DetectPinRegions(),
new DetectPinnedRegions(), // must run after inlining but before non-critical control flow transforms
new LoopDetection(),
new IntroduceExitPoints(),
new ConditionDetection(),
@ -64,7 +64,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -64,7 +64,7 @@ namespace ICSharpCode.Decompiler.CSharp
new ExpressionTransforms(), // must run once before "the loop" to allow RemoveDeadVariablesInit
new RemoveDeadVariableInit(), // must run after ExpressionTransforms because it does not handle stobj(ldloca V, ...)
new DelegateConstruction(),
new LoopingTransform(
new LoopingTransform( // the loop: transforms that cyclicly depend on each other
new ExpressionTransforms(),
new TransformArrayInitializers(),
new ILInlining()

7
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -31,6 +31,7 @@ using ICSharpCode.NRefactory.CSharp.TypeSystem; @@ -31,6 +31,7 @@ using ICSharpCode.NRefactory.CSharp.TypeSystem;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ExpressionType = System.Linq.Expressions.ExpressionType;
namespace ICSharpCode.Decompiler.CSharp
@ -1048,10 +1049,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1048,10 +1049,12 @@ namespace ICSharpCode.Decompiler.CSharp
result = target.UnwrapChild(((DirectionExpression)target.Expression).Expression);
} else {
// Cast pointer type if necessary:
target = target.ConvertTo(new PointerType(inst.Type), this);
if (!TypeUtils.IsCompatibleTypeForMemoryAccess(target.Type, inst.Type)) {
target = target.ConvertTo(new PointerType(inst.Type), this);
}
result = new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression)
.WithoutILInstruction()
.WithRR(new ResolveResult(inst.Type));
.WithRR(new ResolveResult(((TypeWithElementType)target.Type).ElementType));
}
return Assignment(result, value).WithILInstruction(inst);
}

15
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -198,6 +198,21 @@ namespace ICSharpCode.Decompiler.CSharp @@ -198,6 +198,21 @@ namespace ICSharpCode.Decompiler.CSharp
return tryCatch;
}
protected internal override Statement VisitPinnedRegion(PinnedRegion inst)
{
var fixedStmt = new FixedStatement();
fixedStmt.Type = exprBuilder.ConvertType(inst.Variable.Type);
Expression initExpr;
if (inst.Init.OpCode == OpCode.ArrayToPointer) {
initExpr = exprBuilder.Translate(((ArrayToPointer)inst.Init).Array);
} else {
initExpr = exprBuilder.Translate(inst.Init).ConvertTo(inst.Variable.Type, exprBuilder);
}
fixedStmt.Variables.Add(new VariableInitializer(inst.Variable.Name, initExpr).WithILVariable(inst.Variable));
fixedStmt.EmbeddedStatement = Convert(inst.Body);
return fixedStmt;
}
protected internal override Statement VisitBlock(Block block)
{
// Block without container

14
ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs

@ -130,7 +130,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -130,7 +130,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var identExpr = node as IdentifierExpression;
if (identExpr != null) {
var rr = identExpr.GetResolveResult() as ILVariableResolveResult;
if (rr != null && rr.Variable.Kind != VariableKind.Parameter && rr.Variable.Kind != VariableKind.Exception) {
if (rr != null && VariableNeedsDeclaration(rr.Variable.Kind)) {
var newPoint = new InsertionPoint { level = nodeLevel, nextNode = identExpr };
VariableToDeclare v;
if (variableDict.TryGetValue(rr.Variable, out v)) {
@ -145,6 +145,18 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -145,6 +145,18 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
}
bool VariableNeedsDeclaration(VariableKind kind)
{
switch (kind) {
case VariableKind.PinnedLocal:
case VariableKind.Parameter:
case VariableKind.Exception:
return false;
default:
return true;
}
}
/// <summary>
/// Finds an insertion point in a common parent instruction.
/// </summary>

8
ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs

@ -271,13 +271,19 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -271,13 +271,19 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
#if DEBUG
DebugPoint(debugOutputState, inst);
#endif
}
/// <summary>
/// Derived classes may add to this set of flags to ensure they don't forget to override an interesting method.
/// </summary>
protected InstructionFlags flagsRequiringManualImpl = InstructionFlags.ControlFlow | InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable;
protected sealed override void Default(ILInstruction inst)
{
DebugStartPoint(inst);
// This method assumes normal control flow and no branches.
if ((inst.DirectFlags & (InstructionFlags.ControlFlow | InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable)) != 0) {
if ((inst.DirectFlags & flagsRequiringManualImpl) != 0) {
throw new NotImplementedException(GetType().Name + " is missing implementation for " + inst.GetType().Name);
}

12
ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs

@ -108,6 +108,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -108,6 +108,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
{
this.scope = scope;
this.variablesWithUninitializedUsage = new BitSet(scope.Variables.Count);
base.flagsRequiringManualImpl |= InstructionFlags.MayReadLocals | InstructionFlags.MayWriteLocals;
Initialize(new State(scope.Variables.Count));
}
@ -141,7 +142,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -141,7 +142,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
protected internal override void VisitStLoc(StLoc inst)
{
base.VisitStLoc(inst);
inst.Value.AcceptVisitor(this);
HandleStore(inst.Variable);
}
@ -151,15 +152,20 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -151,15 +152,20 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
base.BeginTryCatchHandler(inst);
}
protected internal override void VisitPinnedRegion(PinnedRegion inst)
{
inst.Init.AcceptVisitor(this);
HandleStore(inst.Variable);
inst.Body.AcceptVisitor(this);
}
protected internal override void VisitLdLoc(LdLoc inst)
{
base.VisitLdLoc(inst);
EnsureInitialized(inst.Variable);
}
protected internal override void VisitLdLoca(LdLoca inst)
{
base.VisitLdLoca(inst);
EnsureInitialized(inst.Variable);
}
}

14
ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitionsVisitor.cs

@ -264,6 +264,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -264,6 +264,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
throw new ArgumentNullException("analyzedVariables");
this.scope = scope;
this.analyzedVariables = analyzedVariables;
base.flagsRequiringManualImpl |= InstructionFlags.MayWriteLocals;
// Fill `allStores` and `storeIndexMap` and `firstStoreIndexForVariable`.
var storesByVar = FindAllStoresByVariable(scope, analyzedVariables);
@ -309,8 +310,8 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -309,8 +310,8 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
storesByVar[vi] = new List<ILInstruction> { null };
}
foreach (var inst in scope.Descendants) {
ILVariable v;
if (inst.MatchStLoc(out v) || inst.MatchTryCatchHandler(out v)) {
if ((inst.DirectFlags & InstructionFlags.MayWriteLocals) != 0) {
ILVariable v = ((IInstructionWithVariableOperand)inst).Variable;
if (v.Scope == scope && activeVariables[v.IndexInScope]) {
storesByVar[v.IndexInScope].Add(inst);
}
@ -358,7 +359,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -358,7 +359,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
protected internal override void VisitStLoc(StLoc inst)
{
base.VisitStLoc(inst);
inst.Value.AcceptVisitor(this);
HandleStore(inst, inst.Variable);
}
@ -368,6 +369,13 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -368,6 +369,13 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
HandleStore(inst, inst.Variable);
}
protected internal override void VisitPinnedRegion(PinnedRegion inst)
{
inst.Init.AcceptVisitor(this);
HandleStore(inst, inst.Variable);
inst.Body.AcceptVisitor(this);
}
public bool IsAnalyzedVariable(ILVariable v)
{
return v.Scope == scope && analyzedVariables[v.IndexInScope];

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -113,10 +113,8 @@ @@ -113,10 +113,8 @@
<Compile Include="IL\Instructions\InstructionCollection.cs" />
<Compile Include="IL\Instructions\LdLen.cs" />
<Compile Include="IL\Instructions\Leave.cs" />
<Compile Include="IL\Instructions\LocalVarInstructions.cs" />
<Compile Include="IL\Instructions\MemoryInstructions.cs" />
<Compile Include="IL\Instructions\PatternMatching.cs" />
<Compile Include="IL\Instructions\PinnedRegion.cs" />
<Compile Include="IL\Instructions\Return.cs" />
<Compile Include="IL\Instructions\SimpleInstruction.cs" />
<Compile Include="IL\Instructions\SwitchInstruction.cs" />

219
ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.Decompiler.IL.ControlFlow
{
@ -50,7 +51,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -50,7 +51,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// This means this transform must run before LoopDetection.
/// To make our detection job easier, we must run after variable inlining.
/// </summary>
public class DetectPinRegions : IILTransform
public class DetectPinnedRegions : IILTransform
{
public void Run(ILFunction function, ILTransformContext context)
{
@ -58,7 +59,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -58,7 +59,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
SplitBlocksAtWritesToPinnedLocals(container);
DetectNullSafeArrayToPointer(container);
foreach (var block in container.Blocks)
Run(block);
CreatePinnedRegion(block);
container.Blocks.RemoveAll(b => b.Instructions.Count == 0); // remove dummy blocks
}
}
@ -214,16 +215,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -214,16 +215,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
#endregion
void Run(Block block)
#region CreatePinnedRegion
bool CreatePinnedRegion(Block block)
{
// After SplitBlocksAtWritesToPinnedLocals(), only the second-to-last instruction in each block
// can be a write to a pinned local.
var stLoc = block.Instructions.SecondToLastOrDefault() as StLoc;
if (stLoc == null || stLoc.Variable.Kind != VariableKind.PinnedLocal)
return;
return false;
// stLoc is a store to a pinned local.
if (IsNullOrZero(stLoc.Value))
return; // ignore unpin instructions
return false; // ignore unpin instructions
// stLoc is a store that starts a new pinned region
// Collect the blocks to be moved into the region:
@ -231,55 +233,67 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -231,55 +233,67 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
int[] reachedEdgesPerBlock = new int[sourceContainer.Blocks.Count];
Queue<Block> workList = new Queue<Block>();
Block entryBlock = ((Branch)block.Instructions.Last()).TargetBlock;
if (entryBlock.Parent == sourceContainer) {
reachedEdgesPerBlock[entryBlock.ChildIndex]++;
workList.Enqueue(entryBlock);
while (workList.Count > 0) {
Block workItem = workList.Dequeue();
StLoc workStLoc = workItem.Instructions.SecondToLastOrDefault() as StLoc;
int instructionCount;
if (workStLoc != null && workStLoc.Variable == stLoc.Variable && IsNullOrZero(workStLoc.Value)) {
// found unpin instruction: only consider branches prior to that instruction
instructionCount = workStLoc.ChildIndex;
} else {
instructionCount = workItem.Instructions.Count;
}
for (int i = 0; i < instructionCount; i++) {
foreach (var branch in workItem.Instructions[i].Descendants.OfType<Branch>()) {
if (branch.TargetBlock.Parent == sourceContainer) {
Debug.Assert(branch.TargetBlock != block);
if (reachedEdgesPerBlock[branch.TargetBlock.ChildIndex]++ == 0) {
// detected first edge to that block: add block as work item
workList.Enqueue(branch.TargetBlock);
}
if (entryBlock.Parent != sourceContainer) {
// we didn't find a single block to be added to the pinned region
return false;
}
reachedEdgesPerBlock[entryBlock.ChildIndex]++;
workList.Enqueue(entryBlock);
while (workList.Count > 0) {
Block workItem = workList.Dequeue();
StLoc workStLoc = workItem.Instructions.SecondToLastOrDefault() as StLoc;
int instructionCount;
if (workStLoc != null && workStLoc.Variable == stLoc.Variable && IsNullOrZero(workStLoc.Value)) {
// found unpin instruction: only consider branches prior to that instruction
instructionCount = workStLoc.ChildIndex;
} else {
instructionCount = workItem.Instructions.Count;
}
for (int i = 0; i < instructionCount; i++) {
foreach (var branch in workItem.Instructions[i].Descendants.OfType<Branch>()) {
if (branch.TargetBlock.Parent == sourceContainer) {
Debug.Assert(branch.TargetBlock != block);
if (reachedEdgesPerBlock[branch.TargetBlock.ChildIndex]++ == 0) {
// detected first edge to that block: add block as work item
workList.Enqueue(branch.TargetBlock);
}
}
}
}
}
// Validate that all uses of a block consistently are inside or outside the pinned region.
// (we cannot do this anymore after we start moving blocks around)
for (int i = 0; i < sourceContainer.Blocks.Count; i++) {
if (reachedEdgesPerBlock[i] != 0 && reachedEdgesPerBlock[i] != sourceContainer.Blocks[i].IncomingEdgeCount) {
return;
}
// Validate that all uses of a block consistently are inside or outside the pinned region.
// (we cannot do this anymore after we start moving blocks around)
for (int i = 0; i < sourceContainer.Blocks.Count; i++) {
if (reachedEdgesPerBlock[i] != 0 && reachedEdgesPerBlock[i] != sourceContainer.Blocks[i].IncomingEdgeCount) {
return false;
}
}
BlockContainer body = new BlockContainer();
for (int i = 0; i < sourceContainer.Blocks.Count; i++) {
if (reachedEdgesPerBlock[i] > 0) {
body.Blocks.Add(sourceContainer.Blocks[i]); // move block into body
sourceContainer.Blocks[i] = new Block(); // replace with dummy block
// we'll delete the dummy block later
BlockContainer body = new BlockContainer();
for (int i = 0; i < sourceContainer.Blocks.Count; i++) {
if (reachedEdgesPerBlock[i] > 0) {
var innerBlock = sourceContainer.Blocks[i];
Branch br = innerBlock.Instructions.LastOrDefault() as Branch;
if (br != null && br.TargetContainer == sourceContainer && reachedEdgesPerBlock[br.TargetBlock.ChildIndex] == 0) {
// branch that leaves body.
// Should have an instruction that resets the pin; delete that instruction:
StLoc innerStLoc = innerBlock.Instructions.SecondToLastOrDefault() as StLoc;
if (innerStLoc != null && innerStLoc.Variable == stLoc.Variable && IsNullOrZero(innerStLoc.Value)) {
innerBlock.Instructions.RemoveAt(innerBlock.Instructions.Count - 2);
}
}
}
block.Instructions[block.Instructions.Count - 2] = new PinnedRegion(stLoc, body);
} else {
// we didn't find a single block to be added to the pinned region
block.Instructions[block.Instructions.Count - 2] = new PinnedRegion(stLoc, new Nop());
body.Blocks.Add(innerBlock); // move block into body
sourceContainer.Blocks[i] = new Block(); // replace with dummy block
// we'll delete the dummy block later
}
}
block.Instructions.RemoveAt(block.Instructions.Count - 1);
stLoc.ReplaceWith(new PinnedRegion(stLoc.Variable, stLoc.Value, body));
block.Instructions.RemoveAt(block.Instructions.Count - 1); // remove branch into body
ProcessPinnedRegion((PinnedRegion)block.Instructions.Last());
return true;
}
static bool IsNullOrZero(ILInstruction inst)
@ -290,5 +304,122 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -290,5 +304,122 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
return inst.MatchLdcI4(0) || inst.MatchLdNull();
}
#endregion
#region ProcessPinnedRegion
/// <summary>
/// After a pinned region was detected; process its body; replacing the pin variable
/// with a native pointer as far as possible.
/// </summary>
void ProcessPinnedRegion(PinnedRegion pinnedRegion)
{
BlockContainer body = (BlockContainer)pinnedRegion.Body;
if (pinnedRegion.Variable.Type.Kind == TypeKind.ByReference) {
// C# doesn't support a "by reference" variable, so replace it with a native pointer
ILVariable oldVar = pinnedRegion.Variable;
ILVariable newVar = new ILVariable(
VariableKind.PinnedLocal,
new PointerType(((ByReferenceType)oldVar.Type).ElementType),
oldVar.Index);
newVar.Name = oldVar.Name;
oldVar.Scope.Variables.Add(newVar);
ReplacePinnedVar(oldVar, newVar, pinnedRegion);
} else if (pinnedRegion.Variable.Type.IsKnownType(KnownTypeCode.String)) {
// fixing a string
ILVariable nativeVar;
ILInstruction initInst;
// stloc nativeVar(conv o->i (ldloc pinnedVar))
// if (comp(ldloc nativeVar == conv i4->i <sign extend>(ldc.i4 0))) br targetBlock
// br adjustOffsetToStringData
Block targetBlock, adjustOffsetToStringData;
if (body.EntryPoint.IncomingEdgeCount == 1
&& body.EntryPoint.Instructions.Count == 3
&& body.EntryPoint.Instructions[0].MatchStLoc(out nativeVar, out initInst)
&& nativeVar.Type.Kind == TypeKind.Pointer
&& nativeVar.StoreCount == 2
&& initInst.UnwrapConv(ConversionKind.StopGCTracking).MatchLdLoc(pinnedRegion.Variable)
&& IsBranchOnNull(body.EntryPoint.Instructions[1], nativeVar, out targetBlock)
&& targetBlock.Parent == body
&& body.EntryPoint.Instructions[2].MatchBranch(out adjustOffsetToStringData)
&& adjustOffsetToStringData.Parent == body && adjustOffsetToStringData.IncomingEdgeCount == 1
&& IsOffsetToStringDataBlock(adjustOffsetToStringData, nativeVar, targetBlock))
{
// remove old entry point
body.Blocks.RemoveAt(0);
body.Blocks.RemoveAt(adjustOffsetToStringData.ChildIndex);
// make targetBlock the new entry point
body.Blocks.RemoveAt(targetBlock.ChildIndex);
body.Blocks.Insert(0, targetBlock);
pinnedRegion.Init = new ArrayToPointer(pinnedRegion.Init);
ILVariable newVar;
if (nativeVar.Kind == VariableKind.Local) {
newVar = new ILVariable(VariableKind.PinnedLocal, nativeVar.Type, nativeVar.Index);
newVar.Name = nativeVar.Name;
nativeVar.Scope.Variables.Add(newVar);
ReplacePinnedVar(nativeVar, newVar, pinnedRegion);
} else {
newVar = nativeVar;
}
ReplacePinnedVar(pinnedRegion.Variable, newVar, pinnedRegion);
}
}
// Detect nested pinned regions:
foreach (var block in body.Blocks)
CreatePinnedRegion(block);
body.Blocks.RemoveAll(b => b.Instructions.Count == 0); // remove dummy blocks
}
void ReplacePinnedVar(ILVariable oldVar, ILVariable newVar, ILInstruction inst)
{
Conv conv = inst as Conv;
if (conv != null && conv.Kind == ConversionKind.StopGCTracking && conv.Argument.MatchLdLoc(oldVar)) {
// conv ref->i (ldloc oldVar)
// => ldloc newVar
conv.AddILRange(conv.Argument.ILRange);
conv.ReplaceWith(new LdLoc(newVar) { ILRange = conv.ILRange });
return;
}
var iwvo = inst as IInstructionWithVariableOperand;
if (iwvo != null && iwvo.Variable == oldVar) {
iwvo.Variable = newVar;
if (inst is StLoc && oldVar.Type.Kind == TypeKind.ByReference) {
((StLoc)inst).Value = new Conv(((StLoc)inst).Value, PrimitiveType.I, false, Sign.None);
}
}
foreach (var child in inst.Children) {
ReplacePinnedVar(oldVar, newVar, child);
}
}
bool IsBranchOnNull(ILInstruction condBranch, ILVariable nativeVar, out Block targetBlock)
{
targetBlock = null;
// if (comp(ldloc nativeVar == conv i4->i <sign extend>(ldc.i4 0))) br targetBlock
ILInstruction condition, trueInst, left, right;
return condBranch.MatchIfInstruction(out condition, out trueInst)
&& condition.MatchCompEquals(out left, out right)
&& left.MatchLdLoc(nativeVar) && IsNullOrZero(right)
&& trueInst.MatchBranch(out targetBlock);
}
bool IsOffsetToStringDataBlock(Block block, ILVariable nativeVar, Block targetBlock)
{
// stloc nativeVar(add(ldloc nativeVar, conv i4->i <sign extend>(call [Accessor System.Runtime.CompilerServices.RuntimeHelpers.get_OffsetToStringData():System.Int32]())))
// br IL_0011
ILInstruction left, right, value;
return block.Instructions.Count == 2
&& block.Instructions[0].MatchStLoc(nativeVar, out value)
&& value.MatchAdd(out left, out right)
&& left.MatchLdLoc(nativeVar)
&& IsOffsetToStringDataCall(right)
&& block.Instructions[1].MatchBranch(targetBlock);
}
bool IsOffsetToStringDataCall(ILInstruction inst)
{
Call call = inst.UnwrapConv(ConversionKind.SignExtend) as Call;
return call != null && call.Method.FullName == "System.Runtime.CompilerServices.RuntimeHelpers.get_OffsetToStringData";
}
#endregion
}
}

4
ICSharpCode.Decompiler/IL/ILReader.cs

@ -186,7 +186,7 @@ namespace ICSharpCode.Decompiler.IL @@ -186,7 +186,7 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
void Warn(string message)
{
Debug.Fail(message);
Debug.Fail(string.Format("IL_{0:x4}: {1}", reader.Position, message));
}
void MergeStacks(ImmutableStack<ILVariable> a, ImmutableStack<ILVariable> b)
@ -880,6 +880,8 @@ namespace ICSharpCode.Decompiler.IL @@ -880,6 +880,8 @@ namespace ICSharpCode.Decompiler.IL
if (expectedType != inst.ResultType) {
if (expectedType == StackType.I && inst.ResultType == StackType.I4) {
inst = new Conv(inst, PrimitiveType.I, false, Sign.None);
} else if (expectedType == StackType.Ref && inst.ResultType == StackType.I) {
// implicitly start GC tracking
} else if (inst is InvalidInstruction) {
((InvalidInstruction)inst).ExpectedResultType = expectedType;
} else {

205
ICSharpCode.Decompiler/IL/Instructions.cs

@ -39,7 +39,7 @@ namespace ICSharpCode.Decompiler.IL @@ -39,7 +39,7 @@ namespace ICSharpCode.Decompiler.IL
BlockContainer,
/// <summary>A block of IL instructions.</summary>
Block,
/// <summary>A region where a pinned variable is used (initial representation of future fixed statement)</summary>
/// <summary>A region where a pinned variable is used (initial representation of future fixed statement).</summary>
PinnedRegion,
/// <summary>Unary operator that expects an input of type I4. Returns 1 (of type I4) if the input value is 0. Otherwise, returns 0 (of type I4).</summary>
LogicNot,
@ -171,7 +171,8 @@ namespace ICSharpCode.Decompiler.IL @@ -171,7 +171,8 @@ namespace ICSharpCode.Decompiler.IL
LdLen,
/// <summary>Load address of array element.</summary>
LdElema,
/// <summary>Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty.</summary>
/// <summary>Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty.
/// Also used to convert a string to a reference to the first character.</summary>
ArrayToPointer,
/// <summary>Push a typed reference of type class onto the stack.</summary>
MakeRefAny,
@ -578,22 +579,53 @@ namespace ICSharpCode.Decompiler.IL @@ -578,22 +579,53 @@ namespace ICSharpCode.Decompiler.IL
}
}
/// <summary>A region where a pinned variable is used (initial representation of future fixed statement)</summary>
public sealed partial class PinnedRegion : ILInstruction
/// <summary>A region where a pinned variable is used (initial representation of future fixed statement).</summary>
public sealed partial class PinnedRegion : ILInstruction, IInstructionWithVariableOperand
{
public PinnedRegion(ILInstruction pin, ILInstruction body) : base(OpCode.PinnedRegion)
public PinnedRegion(ILVariable variable, ILInstruction init, ILInstruction body) : base(OpCode.PinnedRegion)
{
this.Pin = pin;
Debug.Assert(variable != null);
this.variable = variable;
this.Init = init;
this.Body = body;
}
public override StackType ResultType { get { return StackType.Void; } }
public static readonly SlotInfo PinSlot = new SlotInfo("Pin", canInlineInto: true);
ILInstruction pin;
public ILInstruction Pin {
get { return this.pin; }
ILVariable variable;
public ILVariable Variable {
get { return variable; }
set {
Debug.Assert(value != null);
if (IsConnected)
variable.StoreCount--;
variable = value;
if (IsConnected)
variable.StoreCount++;
}
}
protected override void Connected()
{
base.Connected();
variable.StoreCount++;
}
protected override void Disconnected()
{
variable.StoreCount--;
base.Disconnected();
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Scope));
}
public static readonly SlotInfo InitSlot = new SlotInfo("Init", canInlineInto: true);
ILInstruction init;
public ILInstruction Init {
get { return this.init; }
set {
ValidateChild(value);
SetChildInstruction(ref this.pin, value, 0);
SetChildInstruction(ref this.init, value, 0);
}
}
public static readonly SlotInfo BodySlot = new SlotInfo("Body");
@ -613,7 +645,7 @@ namespace ICSharpCode.Decompiler.IL @@ -613,7 +645,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
case 0:
return this.pin;
return this.init;
case 1:
return this.body;
default:
@ -624,7 +656,7 @@ namespace ICSharpCode.Decompiler.IL @@ -624,7 +656,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
case 0:
this.Pin = value;
this.Init = value;
break;
case 1:
this.Body = value;
@ -637,7 +669,7 @@ namespace ICSharpCode.Decompiler.IL @@ -637,7 +669,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
case 0:
return PinSlot;
return InitSlot;
case 1:
return BodySlot;
default:
@ -647,24 +679,26 @@ namespace ICSharpCode.Decompiler.IL @@ -647,24 +679,26 @@ namespace ICSharpCode.Decompiler.IL
public sealed override ILInstruction Clone()
{
var clone = (PinnedRegion)ShallowClone();
clone.Pin = this.pin.Clone();
clone.Init = this.init.Clone();
clone.Body = this.body.Clone();
return clone;
}
protected override InstructionFlags ComputeFlags()
{
return pin.Flags | body.Flags;
return InstructionFlags.MayWriteLocals | init.Flags | body.Flags;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.None;
return InstructionFlags.MayWriteLocals;
}
}
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
output.Write(' ');
variable.WriteTo(output);
output.Write('(');
this.pin.WriteTo(output);
this.init.WriteTo(output);
output.Write(", ");
this.body.WriteTo(output);
output.Write(')');
@ -1169,6 +1203,30 @@ namespace ICSharpCode.Decompiler.IL @@ -1169,6 +1203,30 @@ namespace ICSharpCode.Decompiler.IL
clone.Body = this.body.Clone();
return clone;
}
ILVariable variable;
public ILVariable Variable {
get { return variable; }
set {
Debug.Assert(value != null);
if (IsConnected)
variable.StoreCount--;
variable = value;
if (IsConnected)
variable.StoreCount++;
}
}
protected override void Connected()
{
base.Connected();
variable.StoreCount++;
}
protected override void Disconnected()
{
variable.StoreCount--;
base.Disconnected();
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitTryCatchHandler(this);
@ -1329,7 +1387,45 @@ namespace ICSharpCode.Decompiler.IL @@ -1329,7 +1387,45 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(variable != null);
this.variable = variable;
}
ILVariable variable;
public ILVariable Variable {
get { return variable; }
set {
Debug.Assert(value != null);
if (IsConnected)
variable.LoadCount--;
variable = value;
if (IsConnected)
variable.LoadCount++;
}
}
protected override void Connected()
{
base.Connected();
variable.LoadCount++;
}
protected override void Disconnected()
{
variable.LoadCount--;
base.Disconnected();
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Scope));
}
public override StackType ResultType { get { return variable.StackType; } }
protected override InstructionFlags ComputeFlags()
{
return InstructionFlags.MayReadLocals;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.MayReadLocals;
}
}
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
@ -1355,6 +1451,35 @@ namespace ICSharpCode.Decompiler.IL @@ -1355,6 +1451,35 @@ namespace ICSharpCode.Decompiler.IL
this.variable = variable;
}
public override StackType ResultType { get { return StackType.Ref; } }
ILVariable variable;
public ILVariable Variable {
get { return variable; }
set {
Debug.Assert(value != null);
if (IsConnected)
variable.AddressCount--;
variable = value;
if (IsConnected)
variable.AddressCount++;
}
}
protected override void Connected()
{
base.Connected();
variable.AddressCount++;
}
protected override void Disconnected()
{
variable.AddressCount--;
base.Disconnected();
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Scope));
}
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
@ -1380,6 +1505,35 @@ namespace ICSharpCode.Decompiler.IL @@ -1380,6 +1505,35 @@ namespace ICSharpCode.Decompiler.IL
this.variable = variable;
this.Value = value;
}
ILVariable variable;
public ILVariable Variable {
get { return variable; }
set {
Debug.Assert(value != null);
if (IsConnected)
variable.StoreCount--;
variable = value;
if (IsConnected)
variable.StoreCount++;
}
}
protected override void Connected()
{
base.Connected();
variable.StoreCount++;
}
protected override void Disconnected()
{
variable.StoreCount--;
base.Disconnected();
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Scope));
}
public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true);
ILInstruction value;
public ILInstruction Value {
@ -1430,11 +1584,11 @@ namespace ICSharpCode.Decompiler.IL @@ -1430,11 +1584,11 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return variable.StackType; } }
protected override InstructionFlags ComputeFlags()
{
return value.Flags;
return InstructionFlags.MayWriteLocals | value.Flags;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.None;
return InstructionFlags.MayWriteLocals;
}
}
public override void WriteTo(ITextOutput output)
@ -3106,7 +3260,8 @@ namespace ICSharpCode.Decompiler.IL @@ -3106,7 +3260,8 @@ namespace ICSharpCode.Decompiler.IL
}
}
/// <summary>Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty.</summary>
/// <summary>Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty.
/// Also used to convert a string to a reference to the first character.</summary>
public sealed partial class ArrayToPointer : ILInstruction
{
public ArrayToPointer(ILInstruction array) : base(OpCode.ArrayToPointer)
@ -4035,15 +4190,17 @@ namespace ICSharpCode.Decompiler.IL @@ -4035,15 +4190,17 @@ namespace ICSharpCode.Decompiler.IL
}
return false;
}
public bool MatchPinnedRegion(out ILInstruction pin, out ILInstruction body)
public bool MatchPinnedRegion(out ILVariable variable, out ILInstruction init, out ILInstruction body)
{
var inst = this as PinnedRegion;
if (inst != null) {
pin = inst.Pin;
variable = inst.Variable;
init = inst.Init;
body = inst.Body;
return true;
}
pin = default(ILInstruction);
variable = default(ILVariable);
init = default(ILInstruction);
body = default(ILInstruction);
return false;
}

79
ICSharpCode.Decompiler/IL/Instructions.tt

@ -49,10 +49,11 @@ @@ -49,10 +49,11 @@
VoidResult, CustomConstructor, CustomVariableName("container")),
new OpCode("Block", "A block of IL instructions.",
CustomConstructor, CustomVariableName("block")),
new OpCode("PinnedRegion", "A region where a pinned variable is used (initial representation of future fixed statement)",
new OpCode("PinnedRegion", "A region where a pinned variable is used (initial representation of future fixed statement).",
ResultType("Void"),
HasVariableOperand("Store"),
CustomChildren(new []{
new ChildInfo("pin") { CanInlineInto = true },
new ChildInfo("init") { CanInlineInto = true },
new ChildInfo("body")
})),
new OpCode("logic.not", "Unary operator that expects an input of type I4. Returns 1 (of type I4) if the input value is 0. Otherwise, returns 0 (of type I4).",
@ -89,7 +90,7 @@ @@ -89,7 +90,7 @@
CustomChildren(new [] {
new ChildInfo("filter"),
new ChildInfo("body"),
}), HasVariableOperand, CustomWriteTo, CustomComputeFlags),
}), HasVariableOperand("Store", generateCheckInvariant: false), CustomWriteTo, CustomComputeFlags),
new OpCode("try.finally", "Try-finally statement",
BaseClass("TryInstruction"), CustomConstructor, CustomWriteTo, CustomComputeFlags),
new OpCode("try.fault", "Try-fault statement",
@ -109,11 +110,11 @@ @@ -109,11 +110,11 @@
new OpCode("conv", "Numeric cast.",
Unary, CustomConstructor),
new OpCode("ldloc", "Loads the value of a local variable. (ldarg/ldloc)",
CustomClassName("LdLoc"), NoArguments, HasVariableOperand, ResultType("variable.StackType")),
CustomClassName("LdLoc"), NoArguments, HasVariableOperand("Load"), ResultType("variable.StackType")),
new OpCode("ldloca", "Loads the address of a local variable. (ldarga/ldloca)",
CustomClassName("LdLoca"), NoArguments, ResultType("Ref"), HasVariableOperand),
CustomClassName("LdLoca"), NoArguments, ResultType("Ref"), HasVariableOperand("Address")),
new OpCode("stloc", "Stores a value into a local variable. (starg/stloc)",
CustomClassName("StLoc"), HasVariableOperand, CustomArguments("value"),
CustomClassName("StLoc"), HasVariableOperand("Store"), CustomArguments("value"),
ResultType("variable.StackType")),
new OpCode("addressof", "Stores the value into an anonymous temporary variable, and returns the address of that variable.",
CustomClassName("AddressOf"), CustomArguments("value"), ResultType("Ref")),
@ -199,7 +200,8 @@ @@ -199,7 +200,8 @@
new OpCode("ldelema", "Load address of array element.",
CustomClassName("LdElema"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true),
MayThrow, 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.",
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.\n"
+ "Also used to convert a string to a reference to the first character.",
CustomArguments("array"), ResultType("Ref")),
new OpCode("mkrefany", "Push a typed reference of type class onto the stack.",
@ -780,16 +782,59 @@ namespace ICSharpCode.Decompiler.IL @@ -780,16 +782,59 @@ namespace ICSharpCode.Decompiler.IL
};
// HasVariableOperand trait: the instruction refers to a local variable
static Action<OpCode> HasVariableOperand = opCode => {
opCode.ConstructorParameters.Add("ILVariable variable");
opCode.ConstructorBody.Add("Debug.Assert(variable != null);");
opCode.ConstructorBody.Add("this.variable = variable;");
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILVariable", Name = "variable", FieldName = "Variable" });
opCode.GenerateWriteTo = true;
opCode.WriteOperand.Add("output.Write(' ');");
opCode.WriteOperand.Add("variable.WriteTo(output);");
opCode.Interfaces.Add("IInstructionWithVariableOperand");
};
static Action<OpCode> HasVariableOperand(string accessType, bool generateCheckInvariant = true)
{
Action<OpCode> action = opCode => {
opCode.ConstructorParameters.Add("ILVariable variable");
opCode.Members.Add("ILVariable variable;");
opCode.ConstructorBody.Add("Debug.Assert(variable != null);");
opCode.ConstructorBody.Add("this.variable = variable;");
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILVariable", Name = "variable", FieldName = "Variable" });
opCode.GenerateWriteTo = true;
opCode.WriteOperand.Add("output.Write(' ');");
opCode.WriteOperand.Add("variable.WriteTo(output);");
opCode.Interfaces.Add("IInstructionWithVariableOperand");
opCode.Members.Add(@"public ILVariable Variable {
get { return variable; }
set {
Debug.Assert(value != null);
if (IsConnected)
variable.AccessCount--;
variable = value;
if (IsConnected)
variable.AccessCount++;
}
}
protected override void Connected()
{
base.Connected();
variable.AccessCount++;
}
protected override void Disconnected()
{
variable.AccessCount--;
base.Disconnected();
}
".Replace("Access", accessType));
if (generateCheckInvariant) {
opCode.Members.Add(@"internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Scope));
}");
}
};
if (accessType == "Load") {
action += HasFlag("InstructionFlags.MayReadLocals");
} else if (accessType == "Store") {
action += HasFlag("InstructionFlags.MayWriteLocals");
} else {
if (accessType != "Address")
throw new ArgumentException();
}
return action;
}
static Action<OpCode> HasFieldOperand = opCode => {
opCode.ConstructorParameters.Add("IField field");

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

@ -76,6 +76,13 @@ namespace ICSharpCode.Decompiler.IL @@ -76,6 +76,13 @@ namespace ICSharpCode.Decompiler.IL
}
}
/// <summary>
/// Gets the BlockContainer that contains the target block.
/// </summary>
public BlockContainer TargetContainer {
get { return (BlockContainer)targetBlock?.Parent; }
}
protected override void Connected()
{
base.Connected();

9
ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs

@ -60,14 +60,7 @@ namespace ICSharpCode.Decompiler.IL @@ -60,14 +60,7 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags()
{
return InstructionFlags.ControlFlow | condition.Flags | CombineFlags(trueInst.Flags, falseInst.Flags);
}
internal static InstructionFlags CombineFlags(InstructionFlags trueFlags, InstructionFlags falseFlags)
{
// the endpoint of the 'if' is only unreachable if both branches have an unreachable endpoint
const InstructionFlags combineWithAnd = InstructionFlags.EndPointUnreachable;
return (trueFlags & falseFlags) | ((trueFlags | falseFlags) & ~combineWithAnd);
return InstructionFlags.ControlFlow | condition.Flags | SemanticHelper.CombineBranches(trueInst.Flags, falseInst.Flags);
}
public override void WriteTo(ITextOutput output)

128
ICSharpCode.Decompiler/IL/Instructions/LocalVarInstructions.cs

@ -1,128 +0,0 @@ @@ -1,128 +0,0 @@
// 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;
using System.Diagnostics;
namespace ICSharpCode.Decompiler.IL
{
partial class LdLoc
{
ILVariable variable;
public ILVariable Variable {
get { return variable; }
set {
Debug.Assert(value != null);
if (IsConnected)
variable.LoadCount--;
variable = value;
if (IsConnected)
variable.LoadCount++;
}
}
protected override void Connected()
{
base.Connected();
variable.LoadCount++;
}
protected override void Disconnected()
{
variable.LoadCount--;
base.Disconnected();
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Scope));
}
}
partial class LdLoca
{
ILVariable variable;
public ILVariable Variable {
get { return variable; }
set {
Debug.Assert(value != null);
if (IsConnected)
variable.AddressCount--;
variable = value;
if (IsConnected)
variable.AddressCount++;
}
}
protected override void Connected()
{
base.Connected();
variable.AddressCount++;
}
protected override void Disconnected()
{
variable.AddressCount--;
base.Disconnected();
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Scope));
}
}
partial class StLoc
{
ILVariable variable;
public ILVariable Variable {
get { return variable; }
set {
Debug.Assert(value != null);
if (IsConnected)
variable.StoreCount--;
variable = value;
if (IsConnected)
variable.StoreCount++;
}
}
protected override void Connected()
{
base.Connected();
variable.StoreCount++;
}
protected override void Disconnected()
{
variable.StoreCount--;
base.Disconnected();
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Scope));
}
}
}

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

@ -154,6 +154,19 @@ namespace ICSharpCode.Decompiler.IL @@ -154,6 +154,19 @@ namespace ICSharpCode.Decompiler.IL
return false;
}
public bool MatchCompEquals(out ILInstruction left, out ILInstruction right)
{
Comp comp = this as Comp;
if (comp != null && comp.Kind == ComparisonKind.Equality) {
left = comp.Left;
right = comp.Right;
return true;
}
left = null;
right = null;
return false;
}
/// <summary>
/// If this instruction is a conversion of the specified kind, return its argument.
/// Otherwise, return the instruction itself.

32
ICSharpCode.Decompiler/IL/Instructions/PinnedRegion.cs

@ -1,32 +0,0 @@ @@ -1,32 +0,0 @@
// Copyright (c) 2016 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;
using System.Diagnostics;
namespace ICSharpCode.Decompiler.IL
{
partial class PinnedRegion
{
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(((StLoc)this.pin).Variable.Kind == VariableKind.PinnedLocal);
}
}
}

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

@ -53,7 +53,7 @@ namespace ICSharpCode.Decompiler.IL @@ -53,7 +53,7 @@ namespace ICSharpCode.Decompiler.IL
{
var sectionFlags = InstructionFlags.ControlFlow;
foreach (var section in Sections) {
sectionFlags = IfInstruction.CombineFlags(sectionFlags, section.Flags);
sectionFlags = SemanticHelper.CombineBranches(sectionFlags, section.Flags);
}
return value.Flags | sectionFlags;
}

32
ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs

@ -84,7 +84,7 @@ namespace ICSharpCode.Decompiler.IL @@ -84,7 +84,7 @@ namespace ICSharpCode.Decompiler.IL
{
var flags = TryBlock.Flags;
foreach (var handler in Handlers)
flags = IfInstruction.CombineFlags(flags, handler.Flags);
flags = SemanticHelper.CombineBranches(flags, handler.Flags);
return flags | InstructionFlags.ControlFlow;
}
@ -150,13 +150,13 @@ namespace ICSharpCode.Decompiler.IL @@ -150,13 +150,13 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags()
{
return filter.Flags | body.Flags | InstructionFlags.ControlFlow;
return filter.Flags | body.Flags | InstructionFlags.ControlFlow | InstructionFlags.MayWriteLocals;
}
public override InstructionFlags DirectFlags {
get {
// the body is not evaluated if the filter returns 0
return InstructionFlags.ControlFlow;
return InstructionFlags.ControlFlow | InstructionFlags.MayWriteLocals;
}
}
@ -174,32 +174,6 @@ namespace ICSharpCode.Decompiler.IL @@ -174,32 +174,6 @@ namespace ICSharpCode.Decompiler.IL
output.Write(' ');
body.WriteTo(output);
}
ILVariable variable;
public ILVariable Variable {
get { return variable; }
set {
Debug.Assert(value != null);
if (IsConnected)
variable.StoreCount--;
variable = value;
if (IsConnected)
variable.StoreCount++;
}
}
protected override void Connected()
{
base.Connected();
variable.StoreCount++;
}
protected override void Disconnected()
{
variable.StoreCount--;
base.Disconnected();
}
}
partial class TryFinally

9
ICSharpCode.Decompiler/IL/SemanticHelper.cs

@ -22,7 +22,12 @@ namespace ICSharpCode.Decompiler.IL @@ -22,7 +22,12 @@ namespace ICSharpCode.Decompiler.IL
{
static class SemanticHelper
{
// TODO: consider moving IfInstruction.CombineFlags here
internal static InstructionFlags CombineBranches(InstructionFlags trueFlags, InstructionFlags falseFlags)
{
// the endpoint of the 'if' is only unreachable if both branches have an unreachable endpoint
const InstructionFlags combineWithAnd = InstructionFlags.EndPointUnreachable;
return (trueFlags & falseFlags) | ((trueFlags | falseFlags) & ~combineWithAnd);
}
/// <summary>
/// Gets whether instruction is pure:
@ -32,7 +37,7 @@ namespace ICSharpCode.Decompiler.IL @@ -32,7 +37,7 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
internal static bool IsPure(InstructionFlags inst)
{
// ControlFlow is fine, internal control flow is pure as long as it's not an infinite loop,
// ControlFlow is fine: internal control flow is pure as long as it's not an infinite loop,
// and infinite loops are impossible without MayBranch.
const InstructionFlags pureFlags = InstructionFlags.MayReadLocals | InstructionFlags.ControlFlow;
return (inst & ~pureFlags) == 0;

5
ICSharpCode.Decompiler/Tests/TestCases/UnsafeCode.cs

@ -34,6 +34,11 @@ public class UnsafeCode @@ -34,6 +34,11 @@ public class UnsafeCode
}
}
public unsafe int* PointerCast(long* p)
{
return (int*)p;
}
public unsafe long ConvertDoubleToLong(double d)
{
return *(long*)(&d);

Loading…
Cancel
Save