diff --git a/ICSharpCode.Decompiler/CSharp/Annotations.cs b/ICSharpCode.Decompiler/CSharp/Annotations.cs index 499f64085..c49ca45ee 100644 --- a/ICSharpCode.Decompiler/CSharp/Annotations.cs +++ b/ICSharpCode.Decompiler/CSharp/Annotations.cs @@ -125,6 +125,17 @@ namespace ICSharpCode.Decompiler.CSharp else return null; } + + public static ILVariable GetILVariable(this VariableInitializer vi) + { + return vi.Annotation(); + } + + public static VariableInitializer WithILVariable(this VariableInitializer vi, ILVariable v) + { + vi.AddAnnotation(v); + return vi; + } } public class ILVariableResolveResult : ResolveResult diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 334870e99..00033b5c1 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -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 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() diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index c56ebb379..772657fde 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -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 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); } diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index e8c078b0b..51fc0c167 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -197,6 +197,21 @@ namespace ICSharpCode.Decompiler.CSharp tryCatch.CatchClauses.Add(new CatchClause { Body = faultBlock }); 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) { diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs index 41fd64165..da2588466 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs @@ -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 } } + bool VariableNeedsDeclaration(VariableKind kind) + { + switch (kind) { + case VariableKind.PinnedLocal: + case VariableKind.Parameter: + case VariableKind.Exception: + return false; + default: + return true; + } + } + /// /// Finds an insertion point in a common parent instruction. /// diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs index 8496cd8c1..ad4b3f4eb 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs @@ -271,13 +271,19 @@ namespace ICSharpCode.Decompiler.FlowAnalysis #if DEBUG DebugPoint(debugOutputState, inst); #endif + } + /// + /// Derived classes may add to this set of flags to ensure they don't forget to override an interesting method. + /// + 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); } diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs index 7de302656..d0fa90c81 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs @@ -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 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 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); } } diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitionsVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitionsVisitor.cs index 74ab60b03..6f214181c 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitionsVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/ReachingDefinitionsVisitor.cs @@ -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 storesByVar[vi] = new List { 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 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 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]; diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index b03b17719..ee16bd6b6 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -113,10 +113,8 @@ - - diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs index 68111b224..7e69975ca 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs @@ -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 /// This means this transform must run before LoopDetection. /// To make our detection job easier, we must run after variable inlining. /// - public class DetectPinRegions : IILTransform + public class DetectPinnedRegions : IILTransform { public void Run(ILFunction function, ILTransformContext context) { @@ -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 } #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 int[] reachedEdgesPerBlock = new int[sourceContainer.Blocks.Count]; Queue workList = new Queue(); 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()) { - 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()) { + 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); + } } + + 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) @@ -290,5 +304,122 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } return inst.MatchLdcI4(0) || inst.MatchLdNull(); } + #endregion + + #region ProcessPinnedRegion + /// + /// After a pinned region was detected; process its body; replacing the pin variable + /// with a native pointer as far as possible. + /// + 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 (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 (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 (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 } } diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 9f5cecff3..66ebbd3ff 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -186,7 +186,7 @@ namespace ICSharpCode.Decompiler.IL /// void Warn(string message) { - Debug.Fail(message); + Debug.Fail(string.Format("IL_{0:x4}: {1}", reader.Position, message)); } void MergeStacks(ImmutableStack a, ImmutableStack b) @@ -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 { diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 434eaf0ef..d5bedce57 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -39,7 +39,7 @@ namespace ICSharpCode.Decompiler.IL BlockContainer, /// A block of IL instructions. Block, - /// A region where a pinned variable is used (initial representation of future fixed statement) + /// A region where a pinned variable is used (initial representation of future fixed statement). PinnedRegion, /// 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). LogicNot, @@ -171,7 +171,8 @@ namespace ICSharpCode.Decompiler.IL LdLen, /// Load address of array element. LdElema, - /// Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty. + /// 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. ArrayToPointer, /// Push a typed reference of type class onto the stack. MakeRefAny, @@ -578,22 +579,53 @@ namespace ICSharpCode.Decompiler.IL } } - /// A region where a pinned variable is used (initial representation of future fixed statement) - public sealed partial class PinnedRegion : ILInstruction + /// A region where a pinned variable is used (initial representation of future fixed statement). + 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 { switch (index) { case 0: - return this.pin; + return this.init; case 1: return this.body; default: @@ -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 { switch (index) { case 0: - return PinSlot; + return InitSlot; case 1: return BodySlot; default: @@ -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 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 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 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 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 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 } } - /// Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty. + /// 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. public sealed partial class ArrayToPointer : ILInstruction { public ArrayToPointer(ILInstruction array) : base(OpCode.ArrayToPointer) @@ -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; } diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index d35b711ca..5bc831d8e 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -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 @@ 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 @@ 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 @@ 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 }; // HasVariableOperand trait: the instruction refers to a local variable - static Action 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 HasVariableOperand(string accessType, bool generateCheckInvariant = true) + { + Action 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 HasFieldOperand = opCode => { opCode.ConstructorParameters.Add("IField field"); diff --git a/ICSharpCode.Decompiler/IL/Instructions/Branch.cs b/ICSharpCode.Decompiler/IL/Instructions/Branch.cs index c9774d21c..042b32ba5 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Branch.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Branch.cs @@ -76,6 +76,13 @@ namespace ICSharpCode.Decompiler.IL } } + /// + /// Gets the BlockContainer that contains the target block. + /// + public BlockContainer TargetContainer { + get { return (BlockContainer)targetBlock?.Parent; } + } + protected override void Connected() { base.Connected(); diff --git a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs index 2b9d636cd..43030cad2 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs @@ -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) diff --git a/ICSharpCode.Decompiler/IL/Instructions/LocalVarInstructions.cs b/ICSharpCode.Decompiler/IL/Instructions/LocalVarInstructions.cs deleted file mode 100644 index 957d74583..000000000 --- a/ICSharpCode.Decompiler/IL/Instructions/LocalVarInstructions.cs +++ /dev/null @@ -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)); - } - } -} diff --git a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs index a2878851e..22a57fddb 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs @@ -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; + } + /// /// If this instruction is a conversion of the specified kind, return its argument. /// Otherwise, return the instruction itself. diff --git a/ICSharpCode.Decompiler/IL/Instructions/PinnedRegion.cs b/ICSharpCode.Decompiler/IL/Instructions/PinnedRegion.cs deleted file mode 100644 index 4a061a8cf..000000000 --- a/ICSharpCode.Decompiler/IL/Instructions/PinnedRegion.cs +++ /dev/null @@ -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); - } - } -} diff --git a/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs index 92ab257cc..c6c8941f5 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs @@ -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; } diff --git a/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs index bfc807d67..1b8eecbf4 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs @@ -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 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 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 diff --git a/ICSharpCode.Decompiler/IL/SemanticHelper.cs b/ICSharpCode.Decompiler/IL/SemanticHelper.cs index 3c465299b..48f709821 100644 --- a/ICSharpCode.Decompiler/IL/SemanticHelper.cs +++ b/ICSharpCode.Decompiler/IL/SemanticHelper.cs @@ -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); + } /// /// Gets whether instruction is pure: @@ -32,7 +37,7 @@ namespace ICSharpCode.Decompiler.IL /// 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; diff --git a/ICSharpCode.Decompiler/Tests/TestCases/UnsafeCode.cs b/ICSharpCode.Decompiler/Tests/TestCases/UnsafeCode.cs index 877be9d50..79d393c1b 100644 --- a/ICSharpCode.Decompiler/Tests/TestCases/UnsafeCode.cs +++ b/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) { return *(long*)(&d);