context: //
  // CodeReader.cs
  //
  // Author:
  //   Jb Evain (jbevain@gmail.com)
  //
  // (C) 2005 - 2007 Jb Evain
  //
  // 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.
  //
  
  namespace Mono.Cecil.Cil {
  
  	using System;
  	using System.Collections;
  	using System.IO;
  
  	using Mono.Cecil;
  	using Mono.Cecil.Metadata;
  	using Mono.Cecil.Signatures;
  
  	sealed class CodeReader : BaseCodeVisitor {
  
  		ReflectionReader m_reflectReader;
  		MetadataRoot m_root;
  		IDictionary m_instructions;
  
  		public CodeReader (ReflectionReader reflectReader)
  		{
  			m_reflectReader = reflectReader;
  			m_root = m_reflectReader.MetadataRoot;
  			m_instructions = new Hashtable ();
  		}
  
  		public override void VisitMethodBody (MethodBody body)
  		{
  			MethodDefinition meth = body.Method;
  			MethodBody methBody = body;
  			BinaryReader br = m_reflectReader.Module.ImageReader.MetadataReader.GetDataReader (meth.RVA);
  
  			// lets read the method
  			int flags = br.ReadByte ();
  			switch (flags & 0x3) {
  			case (int) MethodHeader.TinyFormat :
  				methBody.CodeSize = flags >> 2;
  				methBody.MaxStack = 8;
  				ReadCilBody (methBody, br);
  				break;
  			case (int) MethodHeader.FatFormat :
  				br.BaseStream.Position--;
  				int fatflags = br.ReadUInt16 ();
  				//int headersize = (fatflags >> 12) & 0xf;
  				methBody.MaxStack = br.ReadUInt16 ();
  				methBody.CodeSize = br.ReadInt32 ();
  				methBody.LocalVarToken = br.ReadInt32 ();
  				body.InitLocals = (fatflags & (int) MethodHeader.InitLocals) != 0;
  				if (methBody.LocalVarToken != 0)
  					VisitVariableDefinitionCollection (methBody.Variables);
  				ReadCilBody (methBody, br);
  				if ((fatflags & (int) MethodHeader.MoreSects) != 0)
  					ReadSection (methBody, br);
  				break;
  			}
  		}
  
  		public static uint GetRid (int token)
  		{
  			return (uint) token & 0x00ffffff;
  		}
  
  		public static ParameterDefinition GetParameter (MethodBody body, int index)
  		{
  			if (body.Method.HasThis) {
  				if (index == 0)
  					return body.Method.This;
  				index--;
  			}
  
  			return body.Method.Parameters [index];
  		}
  
  		public static VariableDefinition GetVariable (MethodBody body, int index)
  		{
  			// bug 15727 - newer cecil does the same (in MethodDefinition.GetVariable)
  			var variables = body.Variables;
  			if (index < 0 || index >= variables.Count)
  				return null;
  			return variables [index];
  		}
  
  		void ReadCilBody (MethodBody body, BinaryReader br)
  		{
  			long start = br.BaseStream.Position;
  			Instruction last = null;
  			m_instructions.Clear();
  			InstructionCollection code = body.Instructions;
  			GenericContext context = new GenericContext (body.Method);
  
  			while (br.BaseStream.Position < start + body.CodeSize) {
  				OpCode op;
  				long offset = br.BaseStream.Position - start;
  				int cursor = br.ReadByte ();
  				if (cursor == 0xfe)
  					op = OpCodes.TwoBytesOpCode [br.ReadByte ()];
  				else
  					op = OpCodes.OneByteOpCode [cursor];
  
  				Instruction instr = new Instruction ((int) offset, op);
  				switch (op.OperandType) {
  				case OperandType.InlineNone :
  					break;
  				case OperandType.InlineSwitch :
  					uint length = br.ReadUInt32 ();
  					int [] branches = new int [length];
  					int [] buf = new int [length];
  					for (int i = 0; i < length; i++)
  						buf [i] = br.ReadInt32 ();
  					for (int i = 0; i < length; i++)
  						branches [i] = Convert.ToInt32 (br.BaseStream.Position - start + buf [i]);
  					instr.Operand = branches;
  					break;
  				case OperandType.ShortInlineBrTarget :
  					sbyte sbrtgt = br.ReadSByte ();
  					instr.Operand = Convert.ToInt32 (br.BaseStream.Position - start + sbrtgt);
  					break;
  				case OperandType.InlineBrTarget :
  					int brtgt = br.ReadInt32 ();
  					instr.Operand = Convert.ToInt32 (br.BaseStream.Position - start + brtgt);
  					break;
  				case OperandType.ShortInlineI :
  					if (op == OpCodes.Ldc_I4_S)
  						instr.Operand = br.ReadSByte ();
  					else
  						instr.Operand = br.ReadByte ();
  					break;
  				case OperandType.ShortInlineVar :
  					instr.Operand = GetVariable (body, br.ReadByte ());
  					break;
  				case OperandType.ShortInlineParam :
  					instr.Operand = GetParameter (body, br.ReadByte ());
  					break;
  				case OperandType.InlineSig :
  					instr.Operand = GetCallSiteAt (br.ReadInt32 (), context);
  					break;
  				case OperandType.InlineI :
  					instr.Operand = br.ReadInt32 ();
  					break;
  				case OperandType.InlineVar :
  					instr.Operand = GetVariable (body, br.ReadInt16 ());
  					break;
  				case OperandType.InlineParam :
  					instr.Operand = GetParameter (body, br.ReadInt16 ());
  					break;
  				case OperandType.InlineI8 :
  					instr.Operand = br.ReadInt64 ();
  					break;
  				case OperandType.ShortInlineR :
  					instr.Operand = br.ReadSingle ();
  					break;
  				case OperandType.InlineR :
  					instr.Operand = br.ReadDouble ();
  					break;
  				case OperandType.InlineString :
  					instr.Operand = m_root.Streams.UserStringsHeap [GetRid (br.ReadInt32 ())];
  					break;
  				case OperandType.InlineField :
  				case OperandType.InlineMethod :
  				case OperandType.InlineType :
  				case OperandType.InlineTok :
  					MetadataToken token = new MetadataToken (br.ReadInt32 ());
  					switch (token.TokenType) {
  					case TokenType.TypeDef:
  						instr.Operand = m_reflectReader.GetTypeDefAt (token.RID);
  						break;
  					case TokenType.TypeRef:
  						instr.Operand = m_reflectReader.GetTypeRefAt (token.RID);
  						break;
  					case TokenType.TypeSpec:
  						instr.Operand = m_reflectReader.GetTypeSpecAt (token.RID, context);
  						break;
  					case TokenType.Field:
  						instr.Operand = m_reflectReader.GetFieldDefAt (token.RID);
  						break;
  					case TokenType.Method:
  						instr.Operand = m_reflectReader.GetMethodDefAt (token.RID);
  						break;
  					case TokenType.MethodSpec:
  						instr.Operand = m_reflectReader.GetMethodSpecAt (token.RID, context);
  						break;
  					case TokenType.MemberRef:
  						instr.Operand = m_reflectReader.GetMemberRefAt (token.RID, context);
  						break;
  					default:
  						throw new ReflectionException ("Wrong token: " + token);
  					}
  					break;
  				}
  
  				m_instructions.Add (instr.Offset, instr);
  
  				if (last != null) {
  					last.Next = instr;
  					instr.Previous = last;
  				}
  
  				last = instr;
  
  				code.Add (instr);
  			}
  
  			// resolve branches
  			foreach (Instruction i in code) {
  				switch (i.OpCode.OperandType) {
  				case OperandType.ShortInlineBrTarget:
  				case OperandType.InlineBrTarget:
  					i.Operand = GetInstruction (body, (int) i.Operand);
  					break;
  				case OperandType.InlineSwitch:
  					int [] lbls = (int []) i.Operand;
  					Instruction [] instrs = new Instruction [lbls.Length];
  					for (int j = 0; j < lbls.Length; j++)
  						instrs [j] = GetInstruction (body, lbls [j]);
  					i.Operand = instrs;
  					break;
  				}
  			}
  
  			if (m_reflectReader.SymbolReader != null)
  				m_reflectReader.SymbolReader.Read (body, m_instructions);
  		}
  
  		Instruction GetInstruction (MethodBody body, int offset)
  		{
  			Instruction instruction = m_instructions [offset] as Instruction;
  			if (instruction != null)
  				return instruction;
  
  			return body.Instructions.Outside;
  		}
  
  		void ReadSection (MethodBody body, BinaryReader br)
  		{
  			br.BaseStream.Position += 3;
  			br.BaseStream.Position &= ~3;
  
  			byte flags = br.ReadByte ();
  			if ((flags & (byte) MethodDataSection.FatFormat) == 0) {
  				int length = br.ReadByte () / 12;
  				br.ReadBytes (2);
  
  				for (int i = 0; i < length; i++) {
  					ExceptionHandler eh = new ExceptionHandler (
  						(ExceptionHandlerType) (br.ReadInt16 () & 0x7));
  					eh.TryStart = GetInstruction (body, Convert.ToInt32 (br.ReadInt16 ()));
  					eh.TryEnd = GetInstruction (body, eh.TryStart.Offset + Convert.ToInt32 (br.ReadByte ()));
  					eh.HandlerStart = GetInstruction (body, Convert.ToInt32 (br.ReadInt16 ()));
  					eh.HandlerEnd = GetInstruction (body, eh.HandlerStart.Offset + Convert.ToInt32 (br.ReadByte ()));
  					ReadExceptionHandlerEnd (eh, br, body);
  					body.ExceptionHandlers.Add (eh);
  				}
  			} else {
  				br.BaseStream.Position--;
  				int length = (br.ReadInt32 () >> 8) / 24;
  				if ((flags & (int) MethodDataSection.EHTable) == 0)
  					br.ReadBytes (length * 24);
  				for (int i = 0; i < length; i++) {
  					ExceptionHandler eh = new ExceptionHandler (
  						(ExceptionHandlerType) (br.ReadInt32 () & 0x7));
  					eh.TryStart = GetInstruction (body, br.ReadInt32 ());
  					eh.TryEnd = GetInstruction (body, eh.TryStart.Offset + br.ReadInt32 ());
  
gt: 					eh.HandlerStart = GetInstruction (body, br.ReadInt32 ());