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. 223
      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
else else
return null; 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 public class ILVariableResolveResult : ResolveResult

4
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

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

7
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

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

15
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -197,6 +197,21 @@ namespace ICSharpCode.Decompiler.CSharp
tryCatch.CatchClauses.Add(new CatchClause { Body = faultBlock }); tryCatch.CatchClauses.Add(new CatchClause { Body = faultBlock });
return tryCatch; 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) protected internal override Statement VisitBlock(Block block)
{ {

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

@ -130,7 +130,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var identExpr = node as IdentifierExpression; var identExpr = node as IdentifierExpression;
if (identExpr != null) { if (identExpr != null) {
var rr = identExpr.GetResolveResult() as ILVariableResolveResult; 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 }; var newPoint = new InsertionPoint { level = nodeLevel, nextNode = identExpr };
VariableToDeclare v; VariableToDeclare v;
if (variableDict.TryGetValue(rr.Variable, out v)) { if (variableDict.TryGetValue(rr.Variable, out v)) {
@ -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> /// <summary>
/// Finds an insertion point in a common parent instruction. /// Finds an insertion point in a common parent instruction.
/// </summary> /// </summary>

8
ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs

@ -271,13 +271,19 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
#if DEBUG #if DEBUG
DebugPoint(debugOutputState, inst); DebugPoint(debugOutputState, inst);
#endif #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) protected sealed override void Default(ILInstruction inst)
{ {
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 & flagsRequiringManualImpl) != 0) {
throw new NotImplementedException(GetType().Name + " is missing implementation for " + inst.GetType().Name); 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
{ {
this.scope = scope; this.scope = scope;
this.variablesWithUninitializedUsage = new BitSet(scope.Variables.Count); this.variablesWithUninitializedUsage = new BitSet(scope.Variables.Count);
base.flagsRequiringManualImpl |= InstructionFlags.MayReadLocals | InstructionFlags.MayWriteLocals;
Initialize(new State(scope.Variables.Count)); Initialize(new State(scope.Variables.Count));
} }
@ -141,7 +142,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
protected internal override void VisitStLoc(StLoc inst) protected internal override void VisitStLoc(StLoc inst)
{ {
base.VisitStLoc(inst); inst.Value.AcceptVisitor(this);
HandleStore(inst.Variable); HandleStore(inst.Variable);
} }
@ -151,15 +152,20 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
base.BeginTryCatchHandler(inst); 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) protected internal override void VisitLdLoc(LdLoc inst)
{ {
base.VisitLdLoc(inst);
EnsureInitialized(inst.Variable); EnsureInitialized(inst.Variable);
} }
protected internal override void VisitLdLoca(LdLoca inst) protected internal override void VisitLdLoca(LdLoca inst)
{ {
base.VisitLdLoca(inst);
EnsureInitialized(inst.Variable); EnsureInitialized(inst.Variable);
} }
} }

14
ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitionsVisitor.cs

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

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

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

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

@ -20,6 +20,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.Decompiler.IL.ControlFlow namespace ICSharpCode.Decompiler.IL.ControlFlow
{ {
@ -50,7 +51,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// This means this transform must run before LoopDetection. /// This means this transform must run before LoopDetection.
/// To make our detection job easier, we must run after variable inlining. /// To make our detection job easier, we must run after variable inlining.
/// </summary> /// </summary>
public class DetectPinRegions : IILTransform public class DetectPinnedRegions : IILTransform
{ {
public void Run(ILFunction function, ILTransformContext context) public void Run(ILFunction function, ILTransformContext context)
{ {
@ -58,7 +59,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
SplitBlocksAtWritesToPinnedLocals(container); SplitBlocksAtWritesToPinnedLocals(container);
DetectNullSafeArrayToPointer(container); DetectNullSafeArrayToPointer(container);
foreach (var block in container.Blocks) foreach (var block in container.Blocks)
Run(block); CreatePinnedRegion(block);
container.Blocks.RemoveAll(b => b.Instructions.Count == 0); // remove dummy blocks container.Blocks.RemoveAll(b => b.Instructions.Count == 0); // remove dummy blocks
} }
} }
@ -214,16 +215,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} }
#endregion #endregion
void Run(Block block) #region CreatePinnedRegion
bool CreatePinnedRegion(Block block)
{ {
// After SplitBlocksAtWritesToPinnedLocals(), only the second-to-last instruction in each block // After SplitBlocksAtWritesToPinnedLocals(), only the second-to-last instruction in each block
// can be a write to a pinned local. // can be a write to a pinned local.
var stLoc = block.Instructions.SecondToLastOrDefault() as StLoc; var stLoc = block.Instructions.SecondToLastOrDefault() as StLoc;
if (stLoc == null || stLoc.Variable.Kind != VariableKind.PinnedLocal) if (stLoc == null || stLoc.Variable.Kind != VariableKind.PinnedLocal)
return; return false;
// stLoc is a store to a pinned local. // stLoc is a store to a pinned local.
if (IsNullOrZero(stLoc.Value)) if (IsNullOrZero(stLoc.Value))
return; // ignore unpin instructions return false; // ignore unpin instructions
// stLoc is a store that starts a new pinned region // stLoc is a store that starts a new pinned region
// Collect the blocks to be moved into the region: // Collect the blocks to be moved into the region:
@ -231,55 +233,67 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
int[] reachedEdgesPerBlock = new int[sourceContainer.Blocks.Count]; int[] reachedEdgesPerBlock = new int[sourceContainer.Blocks.Count];
Queue<Block> workList = new Queue<Block>(); Queue<Block> workList = new Queue<Block>();
Block entryBlock = ((Branch)block.Instructions.Last()).TargetBlock; Block entryBlock = ((Branch)block.Instructions.Last()).TargetBlock;
if (entryBlock.Parent == sourceContainer) { if (entryBlock.Parent != sourceContainer) {
reachedEdgesPerBlock[entryBlock.ChildIndex]++; // we didn't find a single block to be added to the pinned region
workList.Enqueue(entryBlock); return false;
while (workList.Count > 0) { }
Block workItem = workList.Dequeue(); reachedEdgesPerBlock[entryBlock.ChildIndex]++;
StLoc workStLoc = workItem.Instructions.SecondToLastOrDefault() as StLoc; workList.Enqueue(entryBlock);
int instructionCount; while (workList.Count > 0) {
if (workStLoc != null && workStLoc.Variable == stLoc.Variable && IsNullOrZero(workStLoc.Value)) { Block workItem = workList.Dequeue();
// found unpin instruction: only consider branches prior to that instruction StLoc workStLoc = workItem.Instructions.SecondToLastOrDefault() as StLoc;
instructionCount = workStLoc.ChildIndex; int instructionCount;
} else { if (workStLoc != null && workStLoc.Variable == stLoc.Variable && IsNullOrZero(workStLoc.Value)) {
instructionCount = workItem.Instructions.Count; // found unpin instruction: only consider branches prior to that instruction
} instructionCount = workStLoc.ChildIndex;
for (int i = 0; i < instructionCount; i++) { } else {
foreach (var branch in workItem.Instructions[i].Descendants.OfType<Branch>()) { instructionCount = workItem.Instructions.Count;
if (branch.TargetBlock.Parent == sourceContainer) { }
Debug.Assert(branch.TargetBlock != block); for (int i = 0; i < instructionCount; i++) {
if (reachedEdgesPerBlock[branch.TargetBlock.ChildIndex]++ == 0) { foreach (var branch in workItem.Instructions[i].Descendants.OfType<Branch>()) {
// detected first edge to that block: add block as work item if (branch.TargetBlock.Parent == sourceContainer) {
workList.Enqueue(branch.TargetBlock); 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) // Validate that all uses of a block consistently are inside or outside the pinned region.
for (int i = 0; i < sourceContainer.Blocks.Count; i++) { // (we cannot do this anymore after we start moving blocks around)
if (reachedEdgesPerBlock[i] != 0 && reachedEdgesPerBlock[i] != sourceContainer.Blocks[i].IncomingEdgeCount) { for (int i = 0; i < sourceContainer.Blocks.Count; i++) {
return; 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++) { BlockContainer body = new BlockContainer();
if (reachedEdgesPerBlock[i] > 0) { for (int i = 0; i < sourceContainer.Blocks.Count; i++) {
body.Blocks.Add(sourceContainer.Blocks[i]); // move block into body if (reachedEdgesPerBlock[i] > 0) {
sourceContainer.Blocks[i] = new Block(); // replace with dummy block var innerBlock = sourceContainer.Blocks[i];
// we'll delete the dummy block later 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);
}
} }
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[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());
} }
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) static bool IsNullOrZero(ILInstruction inst)
@ -290,5 +304,122 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
} }
return inst.MatchLdcI4(0) || inst.MatchLdNull(); 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
/// </summary> /// </summary>
void Warn(string message) 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) void MergeStacks(ImmutableStack<ILVariable> a, ImmutableStack<ILVariable> b)
@ -880,6 +880,8 @@ namespace ICSharpCode.Decompiler.IL
if (expectedType != inst.ResultType) { if (expectedType != inst.ResultType) {
if (expectedType == StackType.I && inst.ResultType == StackType.I4) { if (expectedType == StackType.I && inst.ResultType == StackType.I4) {
inst = new Conv(inst, PrimitiveType.I, false, Sign.None); 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) { } else if (inst is InvalidInstruction) {
((InvalidInstruction)inst).ExpectedResultType = expectedType; ((InvalidInstruction)inst).ExpectedResultType = expectedType;
} else { } else {

205
ICSharpCode.Decompiler/IL/Instructions.cs

@ -39,7 +39,7 @@ namespace ICSharpCode.Decompiler.IL
BlockContainer, BlockContainer,
/// <summary>A block of IL instructions.</summary> /// <summary>A block of IL instructions.</summary>
Block, 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, 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> /// <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, LogicNot,
@ -171,7 +171,8 @@ namespace ICSharpCode.Decompiler.IL
LdLen, LdLen,
/// <summary>Load address of array element.</summary> /// <summary>Load address of array element.</summary>
LdElema, 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, ArrayToPointer,
/// <summary>Push a typed reference of type class onto the stack.</summary> /// <summary>Push a typed reference of type class onto the stack.</summary>
MakeRefAny, MakeRefAny,
@ -578,22 +579,53 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
/// <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>
public sealed partial class PinnedRegion : ILInstruction 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; this.Body = body;
} }
public override StackType ResultType { get { return StackType.Void; } } public override StackType ResultType { get { return StackType.Void; } }
public static readonly SlotInfo PinSlot = new SlotInfo("Pin", canInlineInto: true); ILVariable variable;
ILInstruction pin; public ILVariable Variable {
public ILInstruction Pin { get { return variable; }
get { return this.pin; } 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 { set {
ValidateChild(value); ValidateChild(value);
SetChildInstruction(ref this.pin, value, 0); SetChildInstruction(ref this.init, value, 0);
} }
} }
public static readonly SlotInfo BodySlot = new SlotInfo("Body"); public static readonly SlotInfo BodySlot = new SlotInfo("Body");
@ -613,7 +645,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index) { switch (index) {
case 0: case 0:
return this.pin; return this.init;
case 1: case 1:
return this.body; return this.body;
default: default:
@ -624,7 +656,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index) { switch (index) {
case 0: case 0:
this.Pin = value; this.Init = value;
break; break;
case 1: case 1:
this.Body = value; this.Body = value;
@ -637,7 +669,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
switch (index) { switch (index) {
case 0: case 0:
return PinSlot; return InitSlot;
case 1: case 1:
return BodySlot; return BodySlot;
default: default:
@ -647,24 +679,26 @@ namespace ICSharpCode.Decompiler.IL
public sealed override ILInstruction Clone() public sealed override ILInstruction Clone()
{ {
var clone = (PinnedRegion)ShallowClone(); var clone = (PinnedRegion)ShallowClone();
clone.Pin = this.pin.Clone(); clone.Init = this.init.Clone();
clone.Body = this.body.Clone(); clone.Body = this.body.Clone();
return clone; return clone;
} }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
return pin.Flags | body.Flags; return InstructionFlags.MayWriteLocals | init.Flags | body.Flags;
} }
public override InstructionFlags DirectFlags { public override InstructionFlags DirectFlags {
get { get {
return InstructionFlags.None; return InstructionFlags.MayWriteLocals;
} }
} }
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);
output.Write(' ');
variable.WriteTo(output);
output.Write('('); output.Write('(');
this.pin.WriteTo(output); this.init.WriteTo(output);
output.Write(", "); output.Write(", ");
this.body.WriteTo(output); this.body.WriteTo(output);
output.Write(')'); output.Write(')');
@ -1169,6 +1203,30 @@ namespace ICSharpCode.Decompiler.IL
clone.Body = this.body.Clone(); clone.Body = this.body.Clone();
return 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) public override void AcceptVisitor(ILVisitor visitor)
{ {
visitor.VisitTryCatchHandler(this); visitor.VisitTryCatchHandler(this);
@ -1329,7 +1387,45 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(variable != null); Debug.Assert(variable != null);
this.variable = variable; 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; } } 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) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);
@ -1355,6 +1451,35 @@ namespace ICSharpCode.Decompiler.IL
this.variable = variable; this.variable = variable;
} }
public override StackType ResultType { get { return StackType.Ref; } } 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) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);
@ -1380,6 +1505,35 @@ namespace ICSharpCode.Decompiler.IL
this.variable = variable; this.variable = variable;
this.Value = value; 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); public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true);
ILInstruction value; ILInstruction value;
public ILInstruction Value { public ILInstruction Value {
@ -1430,11 +1584,11 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return variable.StackType; } } public override StackType ResultType { get { return variable.StackType; } }
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
return value.Flags; return InstructionFlags.MayWriteLocals | value.Flags;
} }
public override InstructionFlags DirectFlags { public override InstructionFlags DirectFlags {
get { get {
return InstructionFlags.None; return InstructionFlags.MayWriteLocals;
} }
} }
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
@ -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 sealed partial class ArrayToPointer : ILInstruction
{ {
public ArrayToPointer(ILInstruction array) : base(OpCode.ArrayToPointer) public ArrayToPointer(ILInstruction array) : base(OpCode.ArrayToPointer)
@ -4035,15 +4190,17 @@ namespace ICSharpCode.Decompiler.IL
} }
return false; 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; var inst = this as PinnedRegion;
if (inst != null) { if (inst != null) {
pin = inst.Pin; variable = inst.Variable;
init = inst.Init;
body = inst.Body; body = inst.Body;
return true; return true;
} }
pin = default(ILInstruction); variable = default(ILVariable);
init = default(ILInstruction);
body = default(ILInstruction); body = default(ILInstruction);
return false; return false;
} }

79
ICSharpCode.Decompiler/IL/Instructions.tt

@ -49,10 +49,11 @@
VoidResult, CustomConstructor, CustomVariableName("container")), VoidResult, CustomConstructor, CustomVariableName("container")),
new OpCode("Block", "A block of IL instructions.", new OpCode("Block", "A block of IL instructions.",
CustomConstructor, CustomVariableName("block")), 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"), ResultType("Void"),
HasVariableOperand("Store"),
CustomChildren(new []{ CustomChildren(new []{
new ChildInfo("pin") { CanInlineInto = true }, new ChildInfo("init") { CanInlineInto = true },
new ChildInfo("body") 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).", 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 @@
CustomChildren(new [] { CustomChildren(new [] {
new ChildInfo("filter"), new ChildInfo("filter"),
new ChildInfo("body"), new ChildInfo("body"),
}), HasVariableOperand, CustomWriteTo, CustomComputeFlags), }), HasVariableOperand("Store", generateCheckInvariant: false), CustomWriteTo, CustomComputeFlags),
new OpCode("try.finally", "Try-finally statement", new OpCode("try.finally", "Try-finally statement",
BaseClass("TryInstruction"), CustomConstructor, CustomWriteTo, CustomComputeFlags), BaseClass("TryInstruction"), CustomConstructor, CustomWriteTo, CustomComputeFlags),
new OpCode("try.fault", "Try-fault statement", new OpCode("try.fault", "Try-fault statement",
@ -109,11 +110,11 @@
new OpCode("conv", "Numeric cast.", new OpCode("conv", "Numeric cast.",
Unary, CustomConstructor), Unary, CustomConstructor),
new OpCode("ldloc", "Loads the value of a local variable. (ldarg/ldloc)", 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)", 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)", 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")), 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"), ResultType("Ref")),
@ -199,7 +200,8 @@
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),
MayThrow, ResultType("Ref"), SupportsReadonlyPrefix), 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")), CustomArguments("array"), ResultType("Ref")),
new OpCode("mkrefany", "Push a typed reference of type class onto the stack.", new OpCode("mkrefany", "Push a typed reference of type class onto the stack.",
@ -780,16 +782,59 @@ namespace ICSharpCode.Decompiler.IL
}; };
// HasVariableOperand trait: the instruction refers to a local variable // HasVariableOperand trait: the instruction refers to a local variable
static Action<OpCode> HasVariableOperand = opCode => { static Action<OpCode> HasVariableOperand(string accessType, bool generateCheckInvariant = true)
opCode.ConstructorParameters.Add("ILVariable variable"); {
opCode.ConstructorBody.Add("Debug.Assert(variable != null);"); Action<OpCode> action = opCode => {
opCode.ConstructorBody.Add("this.variable = variable;"); opCode.ConstructorParameters.Add("ILVariable variable");
opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILVariable", Name = "variable", FieldName = "Variable" }); opCode.Members.Add("ILVariable variable;");
opCode.GenerateWriteTo = true; opCode.ConstructorBody.Add("Debug.Assert(variable != null);");
opCode.WriteOperand.Add("output.Write(' ');"); opCode.ConstructorBody.Add("this.variable = variable;");
opCode.WriteOperand.Add("variable.WriteTo(output);"); opCode.MatchParameters.Add(new MatchParamInfo { TypeName = "ILVariable", Name = "variable", FieldName = "Variable" });
opCode.Interfaces.Add("IInstructionWithVariableOperand"); 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 => { static Action<OpCode> HasFieldOperand = opCode => {
opCode.ConstructorParameters.Add("IField field"); opCode.ConstructorParameters.Add("IField field");

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

@ -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() protected override void Connected()
{ {
base.Connected(); base.Connected();

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

@ -60,14 +60,7 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
return InstructionFlags.ControlFlow | condition.Flags | CombineFlags(trueInst.Flags, falseInst.Flags); return InstructionFlags.ControlFlow | condition.Flags | SemanticHelper.CombineBranches(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);
} }
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)

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

@ -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
return false; 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> /// <summary>
/// If this instruction is a conversion of the specified kind, return its argument. /// If this instruction is a conversion of the specified kind, return its argument.
/// Otherwise, return the instruction itself. /// Otherwise, return the instruction itself.

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

@ -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
{ {
var sectionFlags = InstructionFlags.ControlFlow; var sectionFlags = InstructionFlags.ControlFlow;
foreach (var section in Sections) { foreach (var section in Sections) {
sectionFlags = IfInstruction.CombineFlags(sectionFlags, section.Flags); sectionFlags = SemanticHelper.CombineBranches(sectionFlags, section.Flags);
} }
return value.Flags | sectionFlags; return value.Flags | sectionFlags;
} }

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

@ -84,7 +84,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
var flags = TryBlock.Flags; var flags = TryBlock.Flags;
foreach (var handler in Handlers) foreach (var handler in Handlers)
flags = IfInstruction.CombineFlags(flags, handler.Flags); flags = SemanticHelper.CombineBranches(flags, handler.Flags);
return flags | InstructionFlags.ControlFlow; return flags | InstructionFlags.ControlFlow;
} }
@ -150,13 +150,13 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags() protected override InstructionFlags ComputeFlags()
{ {
return filter.Flags | body.Flags | InstructionFlags.ControlFlow; return filter.Flags | body.Flags | InstructionFlags.ControlFlow | InstructionFlags.MayWriteLocals;
} }
public override InstructionFlags DirectFlags { public override InstructionFlags DirectFlags {
get { get {
// the body is not evaluated if the filter returns 0 // 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
output.Write(' '); output.Write(' ');
body.WriteTo(output); 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 partial class TryFinally

9
ICSharpCode.Decompiler/IL/SemanticHelper.cs

@ -22,7 +22,12 @@ namespace ICSharpCode.Decompiler.IL
{ {
static class SemanticHelper 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> /// <summary>
/// Gets whether instruction is pure: /// Gets whether instruction is pure:
@ -32,7 +37,7 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
internal static bool IsPure(InstructionFlags inst) 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. // and infinite loops are impossible without MayBranch.
const InstructionFlags pureFlags = InstructionFlags.MayReadLocals | InstructionFlags.ControlFlow; const InstructionFlags pureFlags = InstructionFlags.MayReadLocals | InstructionFlags.ControlFlow;
return (inst & ~pureFlags) == 0; return (inst & ~pureFlags) == 0;

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

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

Loading…
Cancel
Save