private void CreateSwitchConstruct(CFGBlockLogicalConstruct switchBlock, ILogicalConstruct parentConstruct, SwitchData switchData, DominatorTree dominatorTree) { List<KeyValuePair<CFGBlockLogicalConstruct, List<int>>> cfgSuccessorToLabelsMap = GetOrderedCFGSuccessorToLabelsMap(switchData); Dictionary<ILogicalConstruct, HashSet<ISingleEntrySubGraph>> validCaseEntryToDominatedNodesMap = GetValidCases(dominatorTree, switchBlock); List<CaseLogicalConstruct> orderedCaseConstructs = new List<CaseLogicalConstruct>(); PairList<List<int>, CFGBlockLogicalConstruct> labelsToCFGSuccessorsList = new PairList<List<int>, CFGBlockLogicalConstruct>(); foreach (KeyValuePair<CFGBlockLogicalConstruct, List<int>> cfgSuccessorToLabelsPair in cfgSuccessorToLabelsMap) { ILogicalConstruct successor; HashSet<ISingleEntrySubGraph> dominatedNodes; if (LogicalFlowUtilities.TryGetParentConstructWithGivenParent(cfgSuccessorToLabelsPair.Key, parentConstruct, out successor) && validCaseEntryToDominatedNodesMap.TryGetValue(successor, out dominatedNodes)) { CaseLogicalConstruct newCaseConstruct = new CaseLogicalConstruct(successor); newCaseConstruct.CaseNumbers.AddRange(cfgSuccessorToLabelsPair.Value); newCaseConstruct.Body.UnionWith(dominatedNodes.Cast<ILogicalConstruct>()); newCaseConstruct.AttachCaseConstructToGraph(); orderedCaseConstructs.Add(newCaseConstruct); } else { labelsToCFGSuccessorsList.Add(cfgSuccessorToLabelsPair.Value, cfgSuccessorToLabelsPair.Key); } } CaseLogicalConstruct defaultCase = null; CFGBlockLogicalConstruct defaultCFGSuccessor = GetCFGLogicalConstructFromBlock(switchData.DefaultCase); ILogicalConstruct defaultSuccessor; HashSet<ISingleEntrySubGraph> defaultCaseNodes; if (LogicalFlowUtilities.TryGetParentConstructWithGivenParent(defaultCFGSuccessor, parentConstruct, out defaultSuccessor) && validCaseEntryToDominatedNodesMap.TryGetValue(defaultSuccessor, out defaultCaseNodes)) { defaultCase = new CaseLogicalConstruct(defaultSuccessor); if (HasSuccessors(defaultCaseNodes)) { defaultCase.Body.UnionWith(defaultCaseNodes.Cast<ILogicalConstruct>()); } defaultCase.AttachCaseConstructToGraph(); } SwitchLogicalConstruct theSwitch = SwitchLogicalConstruct.GroupInSwitchConstruct(switchBlock, orderedCaseConstructs, labelsToCFGSuccessorsList, defaultCase, defaultCFGSuccessor); UpdateDominatorTree(dominatorTree, theSwitch); }
private void CreateControllerSwitchData() { V_0 = this.GetIndexOfLastNonNullElement(this.stateToStartBlock); stackVariable6 = V_0 + 1; V_0 = stackVariable6; V_1 = new InstructionBlock[stackVariable6]; V_2 = 0; while (V_2 < V_0) { if (!InstructionBlock.op_Equality(this.stateToStartBlock[V_2], null)) { V_1[V_2] = this.stateToStartBlock[V_2]; } else { V_1[V_2] = this.defaultStateEntry; } V_2 = V_2 + 1; } this.switchData = new Telerik.JustDecompiler.Cil.SwitchData(null, this.defaultStateEntry, V_1); return; }
public StateMachineCFGCleaner(ControlFlowGraph theCFG, SwitchData controllerSwitchData, InstructionBlock newEntryBlock) { this.theCFG = theCFG; this.controllerSwitchData = controllerSwitchData; this.newEntryBlock = newEntryBlock; }
/// <summary> /// Process each switch block to determine which cases lead to try/finally constructs. /// </summary> /// <remarks> /// E.g.: If the cases from 3 to 5 lead to the same try/finally construct then there is a try/finally construct in the MoveNext method /// that covers states 3, 4 and 5. /// </remarks> /// <param name="switchBlockInfo"></param> private bool DetermineExceptionHandlingIntervalsFromSwitchData(SwitchData switchBlockInfo) { //Since the first try/finally that this switch covers can start at state 20, the complier will optimize this by subtracting 20 from the value of //the state field. int stateOffset = 0; Instruction currentInstruction = switchBlockInfo.SwitchBlock.Last.Previous; if (currentInstruction.OpCode.Code == Code.Sub) { currentInstruction = currentInstruction.Previous; if (!StateMachineUtilities.TryGetOperandOfLdc(currentInstruction, out stateOffset)) { return false; } currentInstruction = currentInstruction.Previous; } //The switch instruction block usually looks like this: // //ldarg.0 <- this //ldfld stateField //stloc.0 //ldloc.0 <- currentInstruction //(ldc.i4 stateOffset) //(sub) //switch .... currentInstruction = currentInstruction.Previous.Previous; if (currentInstruction.OpCode.Code != Code.Ldfld || !(currentInstruction.Operand is FieldReference) || !CheckAndSaveStateField(currentInstruction.Operand as FieldReference)) { //sanity check - the state field used by the Dispose method should be the same as the field used by the MoveNext method return false; } //The algorithm works with the presumption that a try/finally construct contains a consecutive sequence of states. InstructionBlock[] orderedCases = switchBlockInfo.OrderedCasesArray; InstructionBlock currentBlock = null; int intervalStart = -1; int intervalEnd = -1; ExceptionHandler exceptionHandler = null; for (int i = 0; i < orderedCases.Length; i++) { InstructionBlock currentCase = GetActualCase(orderedCases[i]); if (currentBlock != null && currentCase == currentBlock) //Current block will be null, if we still haven't found an exception handler. { intervalEnd = i + stateOffset; continue; } ExceptionHandler theHandler; if (!TryGetExceptionHandler(currentCase, out theHandler)) { //There are cases where a state is never reached (i.e. the state field is never assigned this state number). //This can create holes in the exception handlers, but since the states are never reached we can add them to the handler. //(We work with the assumption that if state 3 and state 6 are handled by the same try/finally construct and 4 and 5 are not handled by //any exception handler, then 4 and 5 are unreachable. True in general, but obfuscation can break this assumption.) continue; } //We've found an exception handler. if (currentBlock != null) //If currentBlock != null, then currentBlock != currentCase. This means that we have found a new exception //handler, so we must store the data for the old one. { if (!TryCreateYieldExceptionHandler(intervalStart, intervalEnd, exceptionHandler)) { return false; } } else //Otherwise this is the first handler found for the switch block. { currentBlock = currentCase; } intervalStart = i + stateOffset; exceptionHandler = theHandler; } return currentBlock == null || TryCreateYieldExceptionHandler(intervalStart, intervalEnd, exceptionHandler); //Store the data for the last found exception handler. }
/// <summary> /// Creates the controller switch data using the information gathered during the traversal of the state controller blocks. /// </summary> private void CreateControllerSwitchData() { int index = GetIndexOfLastNonNullElement(stateToStartBlock); InstructionBlock[] finalCasesArray = new InstructionBlock[++index]; //Trim the excess elements of the cases array. for (int i = 0; i < index; i++) { if (stateToStartBlock[i] == null) { finalCasesArray[i] = defaultStateEntry; } else { finalCasesArray[i] = stateToStartBlock[i]; } } this.switchData = new SwitchData(null, defaultStateEntry, finalCasesArray); }
public YieldStateMachineControlFlowRebuilder(MethodSpecificContext moveNextMethodContext, SwitchData controllerSwitchData, FieldDefinition stateField) { this.moveNextMethodContext = moveNextMethodContext; this.switchData = controllerSwitchData; this.stateField = stateField; }
/// <summary> /// Process each switch block to determine which cases lead to try/finally constructs. /// </summary> /// <param name="switchBlockInfo"></param> private bool DetermineExceptionHandlingStatesFromSwitchData(SwitchData switchBlockInfo) { //Since the first try/finally that this switch covers can start at state 20, the complier will optimize this by subtracting 20 from the value of //the state field. int stateOffset = 0; Instruction currentInstruction = switchBlockInfo.SwitchBlock.Last.Previous; if (currentInstruction.OpCode.Code == Code.Sub) { currentInstruction = currentInstruction.Previous; if (!StateMachineUtilities.TryGetOperandOfLdc(currentInstruction, out stateOffset)) { return false; } currentInstruction = currentInstruction.Previous; } InstructionBlock[] orderedCases = switchBlockInfo.OrderedCasesArray; for (int i = 0; i < orderedCases.Length; i++) { InstructionBlock currentCase = GetActualCase(orderedCases[i]); ExceptionHandler theHandler; if (!TryGetExceptionHandler(currentCase, out theHandler)) { continue; } //We've found an exception handler. if (!this.handlerToStatesMap.ContainsKey(theHandler)) { this.handlerToStatesMap.Add(theHandler, new HashSet<int>()); } this.handlerToStatesMap[theHandler].Add(i + stateOffset); } return true; }
private PairList<CFGBlockLogicalConstruct, List<int>> GetOrderedCFGSuccessorToLabelsMap(SwitchData switchData) { //Ugly but effective. Sorry PairList<CFGBlockLogicalConstruct, List<int>> result = new PairList<CFGBlockLogicalConstruct, List<int>>(); Dictionary<InstructionBlock, KeyValuePair<int, List<int>>> blockSuccessorToResultPositionMap = new Dictionary<InstructionBlock, KeyValuePair<int, List<int>>>(); for (int i = 0; i < switchData.OrderedCasesArray.Length; i++) { InstructionBlock instructionBlock = switchData.OrderedCasesArray[i]; if (instructionBlock != switchData.DefaultCase) { KeyValuePair<int, List<int>> positionToLabelListPair; if (!blockSuccessorToResultPositionMap.TryGetValue(instructionBlock, out positionToLabelListPair)) { positionToLabelListPair = new KeyValuePair<int, List<int>>(result.Count, new List<int>()); result.Add(GetCFGLogicalConstructFromBlock(instructionBlock), positionToLabelListPair.Value); blockSuccessorToResultPositionMap.Add(instructionBlock, positionToLabelListPair); } positionToLabelListPair.Value.Add(i); } } return result; }