/// <summary> /// Follows the instructions until the specified stack entry is accessed. /// </summary> /// <param name="startInstruction">The first instruction.</param> /// <param name="stackEntryDistance">The distance of the stack entry from the top of the stack. 0 means right on top.</param> /// <returns>The instruction that pops the stackEntry and the distance of the entry to the top of the stack. If no valid instruction if found the method returns InstructionWithLeave.Empty.</returns> private KeyValuePair <InstructionWithLeave, int> FollowStackEntry(InstructionWithLeave startInstruction, int stackEntryDistance) { Instruction ins = startInstruction.Instruction; while (true) { int pop = ins.GetPopCount(this.Method); int push = ins.GetPushCount(); if (pop > stackEntryDistance) //does this instruction pop the stack entry { return(new KeyValuePair <InstructionWithLeave, int> (startInstruction.Copy(ins), stackEntryDistance)); } stackEntryDistance -= pop; stackEntryDistance += push; //fetch ne next instruction object alternativeNext; Instruction nextInstruction = GetNextInstruction(ins, out alternativeNext); if (nextInstruction == null) { return(new KeyValuePair <InstructionWithLeave, int> ()); //return / throw / endfinally } if (nextInstruction.OpCode.Code == Code.Leave || nextInstruction.OpCode.Code == Code.Leave_S) { return(new KeyValuePair <InstructionWithLeave, int> ()); //leave clears the stack, the entry is gone. } if (alternativeNext != null) //branch / switch { Instruction oneTarget = alternativeNext as Instruction; if (oneTarget != null) //branch { AlternativePaths.AddIfNew(new KeyValuePair <InstructionWithLeave, int> (startInstruction.Copy(oneTarget), stackEntryDistance)); } else //switch { foreach (Instruction switchTarget in (Instruction [])alternativeNext) { AlternativePaths.AddIfNew(new KeyValuePair <InstructionWithLeave, int> (startInstruction.Copy(switchTarget), stackEntryDistance)); } } } if (nextInstruction.OpCode.FlowControl == FlowControl.Branch || nextInstruction.OpCode.FlowControl == FlowControl.Cond_Branch) { AlternativePaths.AddIfNew(new KeyValuePair <InstructionWithLeave, int> (startInstruction.Copy(nextInstruction), stackEntryDistance)); return(new KeyValuePair <InstructionWithLeave, int> ()); //end of block } ins = nextInstruction; } }
/// <summary> /// Iterates over all Instructions inside UsedBy and spawns a new alternative if necessary. /// </summary> /// <param name="start">The first index to progress.</param> private void CheckUsedBy(int start) { for (int ii = start; ii < UsedBy.Count; ii++) { InstructionWithLeave use = UsedBy [ii].Key; StoreSlot slot = GetStoreSlot(use.Instruction); //check if this is a store instruction bool removeFromUseBy = false; //ignore the use if (use.Instruction.OpCode.Code == Code.Castclass) { removeFromUseBy = true; AlternativePaths.AddIfNew(new KeyValuePair <InstructionWithLeave, int> (use.Copy(use.Instruction.Next), 0)); } else if (use.Instruction.OpCode.Code == Code.Pop) //pop is not a valid usage { removeFromUseBy = true; } else if (!slot.IsNone) { if (slot.Type == StoreType.Argument || slot.Type == StoreType.Local) { removeFromUseBy = true; //temporary save } foreach (var ld in this.FindLoad(use.Copy(use.Instruction.Next), slot)) //start searching at the next instruction { AlternativePaths.AddIfNew(new KeyValuePair <InstructionWithLeave, int> (ld.Copy(ld.Instruction.Next), 0)); } } if (removeFromUseBy) { UsedBy.RemoveAt(ii); ii--; } } }
/// <summary> /// Follows the codeflow starting at a given instruction and finds all loads for a given slot. /// Continues and follows all branches until the slot is overwritten or the method returns / throws. /// </summary> /// <param name="insWithLeave">The first instruction to start the search at.</param> /// <param name="slot">The slot to search.</param> /// <returns>An array of instructions that load from the slot.</returns> private List <InstructionWithLeave> FindLoad(InstructionWithLeave insWithLeave, StoreSlot slot) { LoadAlternatives.Clear(); LoadResults.Clear(); LoadAlternatives.Add(insWithLeave); for (int i = 0; i < LoadAlternatives.Count; i++) //loop over all branches, more will get added inside the loop { insWithLeave = LoadAlternatives [i]; //the first instruction of the block (contains the leave stack) Instruction ins = insWithLeave.Instruction; //the current instruction while (ins != null) { if (GetStoreSlot(ins) == slot) //check if the slot gets overwritten { break; } if (slot == GetLoadSlot(ins)) { LoadResults.AddIfNew(insWithLeave.Copy(ins)); //continue, might be loaded again } //we simply branch to every possible catch block. IList <ExceptionHandler> ehc = null; if (Body.HasExceptionHandlers) { ehc = Body.ExceptionHandlers; foreach (ExceptionHandler handler in ehc) { if (handler.HandlerType != ExceptionHandlerType.Catch) { continue; } if (ins.Offset < handler.TryStart.Offset || ins.Offset >= handler.TryEnd.Offset) { continue; } LoadAlternatives.AddIfNew(insWithLeave.Copy(handler.HandlerStart)); } } //Code.Leave leaves a try/catch block. Search for the finally block. if (ins.OpCode.Code == Code.Leave || ins.OpCode.Code == Code.Leave_S) { bool handlerFound = false; if (ehc != null) { foreach (ExceptionHandler handler in ehc) { if (handler.HandlerType != ExceptionHandlerType.Finally) { continue; } if (handler.TryStart.Offset > ins.Offset || handler.TryEnd.Offset <= ins.Offset) { continue; } LoadAlternatives.AddIfNew(insWithLeave.Push(handler.HandlerStart, ins)); //push the leave instruction onto the leave stack handlerFound = true; break; } } if (!handlerFound) //no finally found (try/catch without finally) { LoadAlternatives.AddIfNew(insWithLeave.Copy((Instruction)ins.Operand)); } break; } if (ins.OpCode.Code == Code.Endfinally) //pop the last leave instruction and branch to it { LoadAlternatives.AddIfNew(insWithLeave.Pop()); break; } //fetch the next instruction (s) object alternativeNext; ins = GetNextInstruction(ins, out alternativeNext); if (ins == null) { break; } if (alternativeNext != null) { Instruction oneTarget = alternativeNext as Instruction; if (oneTarget != null) //normal branch { LoadAlternatives.AddIfNew(insWithLeave.Copy(oneTarget)); } else //switch statement { foreach (Instruction switchTarget in (Instruction [])alternativeNext) { LoadAlternatives.AddIfNew(insWithLeave.Copy(switchTarget)); } } } if (ins.OpCode.FlowControl == FlowControl.Branch || ins.OpCode.FlowControl == FlowControl.Cond_Branch) { if (ins.OpCode.Code != Code.Leave && ins.OpCode.Code != Code.Leave_S) { LoadAlternatives.AddIfNew(insWithLeave.Copy(ins)); //add if new, avoid infinity loop break; } } } } return(LoadResults); }