context: /*
      Copyright (C) 2011-2015 de4dot@gmail.com
  
      This file is part of de4dot.
  
      de4dot is free software: you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation, either version 3 of the License, or
      (at your option) any later version.
  
      de4dot is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
  
      You should have received a copy of the GNU General Public License
      along with de4dot.  If not, see <http://www.gnu.org/licenses/>.
  */
  
  using System;
  using System.Collections.Generic;
  using dnlib.DotNet.Emit;
  
  namespace de4dot.blocks {
  	class InstructionListParser {
  		IList<Instruction> instructions;
  		IList<ExceptionHandler> exceptionHandlers;
  		Dictionary<Instruction, int> instrToIndex;
  		Dictionary<int, List<int>> branches;	// key = dest index, value = instrs branching to dest
  
  		public InstructionListParser(IList<Instruction> instructions, IList<ExceptionHandler> exceptionHandlers) {
  			this.instructions = instructions;
  			this.exceptionHandlers = exceptionHandlers;
  			branches = new Dictionary<int, List<int>>();
  
  			CreateInstrToIndex();
  			CreateBranches();
  			CreateExceptionBranches();
  		}
  
  		void CreateInstrToIndex() {
  			instrToIndex = new Dictionary<Instruction, int>();
  
  			for (int i = 0; i < instructions.Count; i++)
  				instrToIndex[instructions[i]] = i;
  		}
  
  		List<int> GetBranchTargetList(int index) {
  			if (!branches.TryGetValue(index, out var targetsList))
  				branches[index] = targetsList = new List<int>();
  			return targetsList;
  		}
  
  		void MarkAsBranchTarget(Instruction instr) {
  			if (instr == null)
  				return;
  
  			int index = instrToIndex[instr];
  			GetBranchTargetList(index);	// Just create the list
  		}
  
  		void CreateExceptionBranches() {
  			foreach (var eh in exceptionHandlers) {
  				MarkAsBranchTarget(eh.TryStart);
  				MarkAsBranchTarget(eh.TryEnd);
  				MarkAsBranchTarget(eh.FilterStart);
  				MarkAsBranchTarget(eh.HandlerStart);
  				MarkAsBranchTarget(eh.HandlerEnd);
  			}
  		}
  
  		void CreateBranches() {
  			for (int i = 0; i < instructions.Count; i++) {
  				var instr = instructions[i];
  
  				List<int> targets = null;
  				switch (instr.OpCode.OperandType) {
  				case OperandType.ShortInlineBrTarget:
  				case OperandType.InlineBrTarget:
  					var targetInstr = instr.Operand as Instruction;
  					if (targetInstr != null)
  						targets = new List<int> { instrToIndex[targetInstr] };
  					break;
  
  				case OperandType.InlineSwitch:
  					var switchTargets = (Instruction[])instr.Operand;
  					targets = new List<int>(switchTargets.Length);
  					for (int j = 0; j < switchTargets.Length; j++) {
  						var target = switchTargets[j];
  						if (target == null)
  							continue;
  						targets.Add(instrToIndex[target]);
  					}
  					break;
  
  				default:
  					switch (instr.OpCode.Code) {
  					case Code.Endfilter:
  					case Code.Endfinally:
  					case Code.Jmp:
  					case Code.Ret:
  					case Code.Rethrow:
  					case Code.Throw:
  						targets = new List<int>();
  						break;
  					}
  					break;
  				}
  
  				if (targets != null) {
  					if (i + 1 < instructions.Count)
  						targets.Add(i + 1);
  					for (int j = 0; j < targets.Count; j++) {
  						int targetIndex = targets[j];
  						GetBranchTargetList(targetIndex).Add(i);
  					}
  				}
  			}
  		}
  
  		void FindBlocks(List<Block> instrToBlock, List<Block> allBlocks) {
  			Block block = null;
  			for (var i = 0; i < instructions.Count; i++) {
  				if (branches.TryGetValue(i, out var branchSources) || block == null) {
  					block = new Block();
  					allBlocks.Add(block);
  				}
  
  				block.Add(new Instr(instructions[i]));
  				instrToBlock.Add(block);
  			}
  		}
  
  		// Fix all branches so they now point to a Block, and not an Instruction. The
  		// block's Targets field is updated, not the Instruction's Operand field.
  		// Also update Block.FallThrough with next Block if last instr falls through.
  		void FixBranchTargets(List<Block> instrToBlock, List<Block> allBlocks) {
  			for (var i = 0; i < allBlocks.Count; i++) {
  				var block = allBlocks[i];
  				var lastInstr = block.LastInstr;
  
  				switch (lastInstr.OpCode.OperandType) {
  				case OperandType.ShortInlineBrTarget:
  				case OperandType.InlineBrTarget:
  					var targetInstr = lastInstr.Operand as Instruction;
  					if (targetInstr != null)
  						block.Targets = new List<Block> { instrToBlock[instrToIndex[targetInstr]] };
  					break;
  
  				case OperandType.InlineSwitch:
  					var switchTargets = (Instruction[])lastInstr.Operand;
  					var newSwitchTargets = new List<Block>();
  					block.Targets = newSwitchTargets;
  					foreach (var target in switchTargets) {
  						if (target != null)
  							newSwitchTargets.Add(instrToBlock[instrToIndex[target]]);
  					}
  					break;
  				}
  
  				if (i + 1 < allBlocks.Count && Instr.IsFallThrough(lastInstr.OpCode))
  					block.FallThrough = allBlocks[i + 1];
  			}
  		}
  
  		// Updates the sources field of each block
  		void FixBlockSources(List<Block> allBlocks) {
  			foreach (var block in allBlocks) {
  				block.UpdateSources();
  			}
  		}
  
  		class EHInfo {
  			public ExceptionHandler eh;
  
  			public EHInfo(ExceptionHandler eh) => this.eh = eh;
  
  			public override int GetHashCode() {
  				int res = eh.TryStart.GetHashCode();
  				if (eh.TryEnd != null)
  					res += eh.TryEnd.GetHashCode();
  				return res;
  			}
  
  			public override bool Equals(object obj) {
  				var other = obj as EHInfo;
  				if (other == null)
  					return false;
  				return ReferenceEquals(eh.TryStart, other.eh.TryStart) &&
  					   ReferenceEquals(eh.TryEnd, other.eh.TryEnd);
  			}
  		}
  
  		List<List<ExceptionHandler>> GetSortedExceptionInfos() {
  			var exInfos = new Dictionary<EHInfo, List<ExceptionHandler>>();
  			foreach (var eh in exceptionHandlers) {
  				if (!exInfos.TryGetValue(new EHInfo(eh), out var handlers))
  					exInfos[new EHInfo(eh)] = handlers = new List<ExceptionHandler>();
  
  				handlers.Add(eh);
  				if (!ReferenceEquals(handlers[0].TryEnd, eh.TryEnd))
  					throw new ApplicationException("Exception handler's try block does not start and end at the same place as the other one.");
  			}
  
  			var exSorted = new List<List<ExceptionHandler>>(exInfos.Values);
  			exSorted.Sort((a, b) => {
  				int ai, bi;
  
  				// Sort in reverse order of TryStart. This is to make sure that nested
  				// try handlers are before the outer try handler.
  				ai = instrToIndex[a[0].TryStart];
  
gt: 				bi = instrToIndex[b[0].TryStart];