using System.IO;
using System.Collections;

namespace PEAPI {

	/**************************************************************************/  
	/// <summary>
	/// Descriptor for an IL instruction
	/// </summary>
	internal abstract class CILInstruction {
		protected static readonly sbyte maxByteVal = 127;
		protected static readonly sbyte minByteVal = -128;
		protected static readonly byte leadByte = 0xFE;
		protected static readonly uint USHeapIndex = 0x70000000;
		protected static readonly int longInstrStart = (int)Op.arglist;
		public bool twoByteInstr = false;
		public uint size = 0;
		public uint offset;

		internal virtual bool Check(MetaData md) 
		{
			return false;
		}

		internal virtual void Write(FileImage output) { }

	}

	internal class CILByte : CILInstruction {
		byte byteVal;

		internal CILByte(byte bVal) 
		{
			byteVal = bVal;
			size = 1;
		}

		internal override void Write(FileImage output) 
		{
			output.Write(byteVal);
		}

	}

	internal class Instr : CILInstruction {
		protected int instr;

		internal Instr(int inst) 
		{
			if (inst >= longInstrStart) {
				instr = inst - longInstrStart;
				twoByteInstr = true;
				size = 2;
			} else {
				instr = inst;
				size = 1;
			}
		}

		internal override void Write(FileImage output) 
		{
			//Console.WriteLine("Writing instruction " + instr + " with size " + size);
			if (twoByteInstr) output.Write(leadByte);
			output.Write((byte)instr);
		}

	}

	internal class IntInstr : Instr {
		int val;
		bool byteNum;

		internal IntInstr(int inst, int num, bool byteSize) : base(inst) 
		{
			val = num;
			byteNum = byteSize;
			if (byteNum) size++;
			else size += 4;
		}

		internal sealed override void Write(FileImage output) 
		{
			base.Write(output);
			if (byteNum) 
				output.Write((sbyte)val);
			else 
				output.Write(val); 
		}

	}

	internal class UIntInstr : Instr {
		int val;
		bool byteNum;

		internal UIntInstr(int inst, int num, bool byteSize) : base(inst) 
		{
			val = num;
			byteNum = byteSize;
			if (byteNum) size++;
			else size += 2;
		}

		internal sealed override void Write(FileImage output) 
		{
			base.Write(output);
			if (byteNum)
				output.Write((byte)val);
			else
				output.Write((ushort)val); 
		}

	}

	internal class LongInstr : Instr {
		long val;

		internal LongInstr(int inst, long l) : base(inst) 
		{
			val = l;
			size += 8;
		}

		internal sealed override void Write(FileImage output) 
		{
			base.Write(output);
			output.Write(val);
		}

	}

	internal class FloatInstr : Instr {
		float fVal;

		internal FloatInstr(int inst, float f) : base(inst) 
		{
			fVal = f;
			size += 4;
		}

		internal sealed override void Write(FileImage output) 
		{
			base.Write(output);
			output.Write(fVal);
		}

	}

	internal class DoubleInstr : Instr {
		double val;

		internal DoubleInstr(int inst, double d) : base(inst) 
		{
			val = d;
			size += 8;
		}

		internal sealed override void Write(FileImage output) 
		{
			base.Write(output);
			output.Write(val);
		}

	}

	internal class StringInstr : Instr {
		string val;
		byte[] bval;                                                  
		uint strIndex;

		internal StringInstr(int inst, string str) : base(inst) 
		{
			val = str;  
			size += 4;
		}

		internal StringInstr (int inst, byte[] str) : base (inst) 
		{
			bval = str;
			size += 4;
		}

		internal sealed override bool Check(MetaData md) 
		{
			if (val != null)
				strIndex = md.AddToUSHeap(val);
			else
				strIndex = md.AddToUSHeap (bval);
			return false;
		}

		internal sealed override void Write(FileImage output) 
		{
			base.Write(output);
			output.Write(USHeapIndex  | strIndex);
		}

	}

	internal class LabelInstr : CILInstruction {
		CILLabel label;

		internal LabelInstr(CILLabel lab) 
		{
			label = lab;
			label.AddLabelInstr(this);
		}
	}

	internal class FieldInstr : Instr {
		Field field;

		internal FieldInstr(int inst, Field f) : base(inst) 
		{
			field = f;
			size += 4;
		}

		internal sealed override void Write(FileImage output) 
		{
			base.Write(output);
			output.Write(field.Token());
		}

	}

	internal class MethInstr : Instr {
		Method meth;

		internal MethInstr(int inst, Method m) : base(inst) 
		{
			meth = m;
			size += 4;
		}

		internal sealed override void Write(FileImage output) 
		{
			base.Write(output);
			output.Write(meth.Token());
		}

	}

	internal class SigInstr : Instr {
		CalliSig signature;

		internal SigInstr(int inst, CalliSig sig) : base(inst) 
		{
			signature = sig;
			size += 4;
		}

		internal sealed override bool Check(MetaData md) 
		{
			md.AddToTable(MDTable.StandAloneSig,signature);
			signature.BuildTables(md);
			return false;
		}

		internal sealed override void Write(FileImage output) 
		{
			base.Write(output);
			output.Write(signature.Token());
		}
	}

	internal class TypeInstr : Instr {
		MetaDataElement theType;

		internal TypeInstr(int inst, Type aType, MetaData md) : base(inst) 
		{
			theType = aType.GetTypeSpec(md);
			size += 4;
		}

		internal sealed override void Write(FileImage output) 
		{
			base.Write(output);
			output.Write(theType.Token());
		}

	}

	internal class BranchInstr : Instr {
		CILLabel dest;
		private bool shortVer = true;
		private int target = 0;

		internal BranchInstr(int inst, CILLabel dst) : base(inst) 
		{
			dest = dst;
			dest.AddBranch(this);
			size++;

			if (inst >= (int) BranchOp.br && inst != (int) BranchOp.leave_s) {
				shortVer = false;
				size += 3;
			}
		}

		internal sealed override bool Check(MetaData md) 
		{
			target = (int)dest.GetLabelOffset() - (int)(offset + size);
			return false;
		}

		internal sealed override void Write(FileImage output) 
		{
			base.Write(output);
			if (shortVer)
				output.Write((sbyte)target);
			else
				output.Write(target);
		}

	}

	internal class SwitchInstr : Instr {
		CILLabel[] cases;
		uint numCases = 0;

		internal SwitchInstr(int inst, CILLabel[] dsts) : base(inst) 
		{
			cases = dsts;
			if (cases != null) numCases = (uint)cases.Length;
			size += 4 + (numCases * 4);
			for (int i=0; i < numCases; i++) {
				cases[i].AddBranch(this);
			}
		}

		internal sealed override void Write(FileImage output) 
		{
			base.Write(output);
			output.Write(numCases);
			for (int i=0; i < numCases; i++) {
				int target = (int)cases[i].GetLabelOffset() - (int)(offset + size);
				output.Write(target);
			}
		}

	}

	/**************************************************************************/  
	/// <summary>
	/// The IL instructions for a method
	/// </summary>
	public class CILInstructions  {
		private static readonly uint ExHeaderSize = 4;
		private static readonly uint FatExClauseSize = 24;
		private static readonly uint SmlExClauseSize = 12;
		private static readonly sbyte maxByteVal = 127;
		private static readonly sbyte minByteVal = -128;
		private static readonly byte maxUByteVal = 255;
		private static readonly int smallSize = 64;
		private static readonly ushort TinyFormat = 0x2;
		private static readonly ushort FatFormat = 0x3003;
		private static readonly ushort MoreSects = 0x8;
		private static readonly ushort InitLocals = 0x10;
		private static readonly uint FatSize = 12;
		private static readonly byte FatExceptTable = 0x41;
		private static readonly byte SmlExceptTable = 0x01; 

		private MetaData metaData;
		private ArrayList exceptions, blockStack;
		//private bool codeChecked = false;
		private static readonly int INITSIZE = 5;
		private CILInstruction[] buffer = new CILInstruction[INITSIZE];
		private int tide = 0;
		private uint offset = 0;
		private ushort headerFlags = 0;
		private short maxStack;
		private uint paddingNeeded = 0;
		private byte exceptHeader = 0;
		uint localSigIx = 0;
		uint codeSize = 0, exceptSize = 0;
		bool tinyFormat, fatExceptionFormat = false;

		public uint Offset {
			get { return offset; }
		}	

		internal CILInstructions(MetaData md) 
		{
			metaData = md;
		}

		private void AddToBuffer(CILInstruction inst) 
		{
			if (tide >= buffer.Length) {
				CILInstruction[] tmp = buffer;
				buffer = new CILInstruction[tmp.Length * 2];
				for (int i=0; i < tide; i++) {
					buffer[i] = tmp[i];
				}
			}
			//Console.WriteLine("Adding instruction at offset " + offset + " with size " + inst.size);
			inst.offset = offset;
			offset += inst.size;
			buffer[tide++] = inst;
		}

		/// <summary>
		/// Add a simple IL instruction
		/// </summary>
		/// <param name="inst">the IL instruction</param>
		public void Inst(Op inst) 
		{
			AddToBuffer(new Instr((int)inst));
		}

		/// <summary>
		/// Add an IL instruction with an integer parameter
		/// </summary>
		/// <param name="inst">the IL instruction</param>
		/// <param name="val">the integer parameter value</param>
		public void IntInst(IntOp inst, int val) 
		{
			int instr = (int)inst;
			if ((inst == IntOp.ldc_i4_s) || (inst == IntOp.ldc_i4)) 
				AddToBuffer(new IntInstr(instr,val,(inst == IntOp.ldc_i4_s)));
			else
				AddToBuffer(new UIntInstr(instr,val,((inst < IntOp.ldc_i4_s) ||
								(inst == IntOp.unaligned))));
		}

		/// <summary>
		/// Add the load long instruction
		/// </summary>
		/// <param name="cVal">the long value</param>
		public void ldc_i8(long cVal) 
		{
			AddToBuffer(new LongInstr(0x21,cVal));
		}

		/// <summary>
		/// Add the load float32 instruction
		/// </summary>
		/// <param name="cVal">the float value</param>
		public void ldc_r4(float cVal) 
		{
			AddToBuffer(new FloatInstr(0x22,cVal));
		}

		/// <summary>
		/// Add the load float64 instruction
		/// </summary>
		/// <param name="cVal">the float value</param>
		public void ldc_r8(double cVal) 
		{
			AddToBuffer(new DoubleInstr(0x23,cVal));
		}

		/// <summary>
		/// Add the load string instruction
		/// </summary>
		/// <param name="str">the string value</param>
		public void ldstr(string str) 
		{
			AddToBuffer(new StringInstr(0x72,str));
		}

		/// <summary>
		/// Add the load string instruction
		/// </summary>
		public void ldstr (byte[] str) 
		{
			AddToBuffer (new StringInstr (0x72, str));
		}

		/// <summary>
		/// Add the calli instruction
		/// </summary>
		/// <param name="sig">the signature for the calli</param>
		public void calli(CalliSig sig) 
		{
			AddToBuffer(new SigInstr(0x29,sig));
		}

		/// <summary>
		/// Add a label to the CIL instructions
		/// </summary>
		/// <param name="lab">the label to be added</param>
		public void CodeLabel(CILLabel lab) 
		{
			AddToBuffer(new LabelInstr(lab));
		}

		/// <summary>
		/// Add an instruction with a field parameter
		/// </summary>
		/// <param name="inst">the CIL instruction</param>
		/// <param name="f">the field parameter</param>
		public void FieldInst(FieldOp inst, Field f) 
		{
			AddToBuffer(new FieldInstr((int)inst,f));
		}

		/// <summary>
		/// Add an instruction with a method parameter
		/// </summary>
		/// <param name="inst">the CIL instruction</param>
		/// <param name="m">the method parameter</param>
		public void MethInst(MethodOp inst, Method m) 
		{
			AddToBuffer(new MethInstr((int)inst,m));
		}

		/// <summary>
		/// Add an instruction with a type parameter
		/// </summary>
		/// <param name="inst">the CIL instruction</param>
		/// <param name="t">the type argument for the CIL instruction</param>
		public void TypeInst(TypeOp inst, Type aType) 
		{
			AddToBuffer(new TypeInstr((int)inst,aType,metaData));
		}

		/// <summary>
		/// Add a branch instruction
		/// </summary>
		/// <param name="inst">the branch instruction</param>
		/// <param name="lab">the label that is the target of the branch</param>
		public void Branch(BranchOp inst,  CILLabel lab) 
		{
			AddToBuffer(new BranchInstr((int)inst,lab));
		}

		/// <summary>
		/// Add a switch instruction
		/// </summary>
		/// <param name="labs">the target labels for the switch</param>
		public void Switch(CILLabel[] labs) 
		{
			AddToBuffer(new SwitchInstr(0x45,labs));
		}

		/// <summary>
		/// Add a byte to the CIL instructions (.emitbyte)
		/// </summary>
		/// <param name="bVal"></param>
		public void emitbyte(byte bVal) 
		{
			AddToBuffer(new CILByte(bVal));
		}

		/// <summary>
		/// Add an instruction which puts an integer on TOS.  This method
		/// selects the correct instruction based on the value of the integer.
		/// </summary>
		/// <param name="i">the integer value</param>
		public void PushInt(int i) 
		{
			if (i == -1) {
				AddToBuffer(new Instr((int)Op.ldc_i4_m1));
			} else if ((i >= 0) && (i <= 8)) {
				Op op = (Op)(Op.ldc_i4_0 + i);
				AddToBuffer(new Instr((int)op));
			} else if ((i >= minByteVal) && (i <= maxByteVal)) {
				AddToBuffer(new IntInstr((int)IntOp.ldc_i4_s,i,true));
			} else {
				AddToBuffer(new IntInstr((int)IntOp.ldc_i4,i,false)); 
			}
		}

		/// <summary>
		/// Add the instruction to load a long on TOS
		/// </summary>
		/// <param name="l">the long value</param>
		public void PushLong(long l) 
		{
			AddToBuffer(new LongInstr(0x21,l));
		}

		/// <summary>
		/// Add an instruction to push the boolean value true on TOS
		/// </summary>
		public void PushTrue() 
		{
			AddToBuffer(new Instr((int)Op.ldc_i4_1));
		}

		/// <summary>
		///  Add an instruction to push the boolean value false on TOS
		/// </summary>
		public void PushFalse() 
		{
			AddToBuffer(new Instr((int)Op.ldc_i4_0));
		}

		/// <summary>
		/// Add the instruction to load an argument on TOS.  This method
		/// selects the correct instruction based on the value of argNo
		/// </summary>
		/// <param name="argNo">the number of the argument</param>
		public void LoadArg(int argNo) 
		{
			if (argNo < 4) {
				int op = (int)Op.ldarg_0 + argNo;
				AddToBuffer(new Instr(op));
			} else if (argNo <= maxUByteVal) {
				AddToBuffer(new UIntInstr((int)IntOp.ldarg,argNo,true));
			} else {
				AddToBuffer(new UIntInstr(0x09,argNo,false)); 
			}
		}

		/// <summary>
		/// Add the instruction to load the address of an argument on TOS.
		/// This method selects the correct instruction based on the value
		/// of argNo.
		/// </summary>
		/// <param name="argNo">the number of the argument</param>
		public void LoadArgAdr(int argNo) 
		{
			if (argNo <= maxUByteVal) {
				AddToBuffer(new UIntInstr((int)IntOp.ldarga,argNo,true));
			} else {
				AddToBuffer(new UIntInstr(0x0A,argNo,false)); 
			}
		}

		/// <summary>
		/// Add the instruction to load a local on TOS.  This method selects
		/// the correct instruction based on the value of locNo.
		/// </summary>
		/// <param name="locNo">the number of the local to load</param>
		public void LoadLocal(int locNo) 
		{
			if (locNo < 4) {
				int op = (int)Op.ldloc_0 + locNo;
				AddToBuffer(new Instr(op));
			} else if (locNo <= maxUByteVal) {
				AddToBuffer(new UIntInstr((int)IntOp.ldloc,locNo,true));
			} else {
				AddToBuffer(new UIntInstr(0x0C,locNo,false)); 
			}
		}

		/// <summary>
		/// Add the instruction to load the address of a local on TOS.
		/// This method selects the correct instruction based on the 
		/// value of locNo.
		/// </summary>
		/// <param name="locNo">the number of the local</param>
		public void LoadLocalAdr(int locNo) 
		{
			if (locNo <= maxUByteVal) {
				AddToBuffer(new UIntInstr((int)IntOp.ldloca,locNo,true));
			} else {
				AddToBuffer(new UIntInstr(0x0D,locNo,false)); 
			}
		}

		/// <summary>
		/// Add the instruction to store to an argument.  This method
		/// selects the correct instruction based on the value of argNo.
		/// </summary>
		/// <param name="argNo">the argument to be stored to</param>
		public void StoreArg(int argNo) 
		{
			if (argNo <= maxUByteVal) {
				AddToBuffer(new UIntInstr((int)IntOp.starg,argNo,true));
			} else {
				AddToBuffer(new UIntInstr(0x0B,argNo,false)); 
			}
		}

		/// <summary>
		/// Add the instruction to store to a local.  This method selects
		/// the correct instruction based on the value of locNo.
		/// </summary>
		/// <param name="locNo">the local to be stored to</param>
		public void StoreLocal(int locNo) 
		{
			if (locNo < 4) {
				int op = (int)Op.stloc_0 + locNo;
				AddToBuffer(new Instr(op));
			} else if (locNo <= maxUByteVal) {
				AddToBuffer(new UIntInstr((int)IntOp.stloc,locNo,true));
			} else {
				AddToBuffer(new UIntInstr(0x0E,locNo,false)); 
			}
		}

		/// <summary>
		/// Create a new CIL label.  To place the label in the CIL instruction
		/// stream use CodeLabel.
		/// </summary>
		/// <returns>a new CIL label</returns>
		public CILLabel NewLabel() 
		{
			return new CILLabel();
		}

		public void AddTryBlock(TryBlock tryBlock) 
		{
			if (exceptions == null) 
				exceptions = new ArrayList();
			else if (exceptions.Contains(tryBlock)) return;
			exceptions.Add(tryBlock);
			tryBlock.ResolveCatchBlocks (metaData);
		}

		/// <summary>
		/// Create a new label at this position in the code buffer
		/// </summary>
		/// <returns>the label at the current position</returns>
		public CILLabel NewCodedLabel() 
		{
			CILLabel lab = new CILLabel();
			AddToBuffer(new LabelInstr(lab));
			return lab;
		}

		/// <summary>
		/// Mark this position as the start of a new block
		/// (try, catch, filter, finally or fault)
		/// </summary>
		public void StartBlock() 
		{
			if (blockStack == null) blockStack = new ArrayList();
			blockStack.Insert(0,NewCodedLabel());
		}

		/// <summary>
		/// Mark this position as the end of the last started block and
		/// make it a try block.  This try block is added to the current 
		/// instructions (ie do not need to call AddTryBlock)
		/// </summary>
		/// <returns>The try block just ended</returns>
		public TryBlock EndTryBlock() 
		{
			TryBlock tBlock = new TryBlock((CILLabel)blockStack[0],NewCodedLabel());
			blockStack.RemoveAt(0);
			AddTryBlock(tBlock);
			return tBlock;
		}

		/// <summary>
		/// Mark this position as the end of the last started block and
		/// make it a catch block.  This catch block is associated with the
		/// specified try block.
		/// </summary>
		/// <param name="exceptType">the exception type to be caught</param>
		/// <param name="tryBlock">the try block associated with this catch block</param>
		public void EndCatchBlock(Class exceptType, TryBlock tryBlock) 
		{
			Catch catchBlock = new Catch(exceptType,(CILLabel)blockStack[0],
					NewCodedLabel());
			tryBlock.AddHandler(catchBlock);
		}

		/// <summary>
		/// Mark this position as the end of the last started block and
		/// make it a filter block.  This filter block is associated with the
		/// specified try block.
		/// </summary>
		/// <param name="filterLab">the label where the filter code is</param>
		/// <param name="tryBlock">the try block associated with this filter block</param>
		public void EndFilterBlock(CILLabel filterLab, TryBlock tryBlock) 
		{
			Filter filBlock = new Filter(filterLab,(CILLabel)blockStack[0],NewCodedLabel());
			tryBlock.AddHandler(filBlock);
		}

		/// <summary>
		/// Mark this position as the end of the last started block and
		/// make it a finally block.  This finally block is associated with the
		/// specified try block.
		/// </summary>
		/// <param name="tryBlock">the try block associated with this finally block</param>
		public void EndFinallyBlock(TryBlock tryBlock) 
		{
			Finally finBlock= new Finally((CILLabel)blockStack[0],NewCodedLabel());
			tryBlock.AddHandler(finBlock);
		}

		/// <summary>
		/// Mark this position as the end of the last started block and
		/// make it a fault block.  This fault block is associated with the
		/// specified try block.
		/// </summary>
		/// <param name="tryBlock">the try block associated with this fault block</param>
		public void EndFaultBlock(TryBlock tryBlock) 
		{
			Fault fBlock= new Fault((CILLabel)blockStack[0],NewCodedLabel());
			tryBlock.AddHandler(fBlock);
		}

		internal uint GetCodeSize() 
		{
			return codeSize + paddingNeeded + exceptSize;
		}

		internal void CheckCode(uint locSigIx, bool initLocals, int maxStack) 
		{
			if (tide == 0) return;
			bool changed = true;
			while (changed) {
				changed = false;
				for (int i=0; i < tide; i++) {
					changed = buffer[i].Check(metaData) || changed;
				}
				if (changed) {
					for (int i=1; i < tide; i++) {
						buffer[i].offset = buffer[i-1].offset + buffer[i-1].size;
					}
					offset = buffer[tide-1].offset + buffer[tide-1].size;
				}
			}
			codeSize = offset;
			// Console.WriteLine("codeSize before header added = " + codeSize);
			if ((offset < smallSize) && (maxStack <= 8) && (locSigIx == 0) && (exceptions == null)) {
				// can use tiny header
				//Console.WriteLine("Tiny Header");
				tinyFormat = true;
				headerFlags = (ushort)(TinyFormat | ((ushort)codeSize << 2));
				codeSize++;
				if ((codeSize % 4) != 0) { paddingNeeded = 4 - (codeSize % 4); }
			} else {
				//Console.WriteLine("Fat Header");
				tinyFormat = false;
				localSigIx = locSigIx;
				this.maxStack = (short)maxStack;
				headerFlags = FatFormat;
				if (exceptions != null) {
					// Console.WriteLine("Got exceptions");
					headerFlags |= MoreSects;
					uint numExceptClauses = 0;
					for (int i=0; i < exceptions.Count; i++) {
						TryBlock tryBlock = (TryBlock)exceptions[i];
						tryBlock.SetSize();
						numExceptClauses += (uint)tryBlock.NumHandlers();
						if (tryBlock.isFat()) fatExceptionFormat = true;
					}

					uint data_size = ExHeaderSize + numExceptClauses *
						(fatExceptionFormat ? FatExClauseSize : SmlExClauseSize);

					if (data_size > 255)
						fatExceptionFormat = true;

					// Console.WriteLine("numexceptclauses = " + numExceptClauses);
					if (fatExceptionFormat) {
						// Console.WriteLine("Fat exception format");
						exceptHeader = FatExceptTable;
						exceptSize = ExHeaderSize + numExceptClauses * FatExClauseSize;
					} else {
						// Console.WriteLine("Tiny exception format");
						exceptHeader = SmlExceptTable;
						exceptSize = ExHeaderSize + numExceptClauses * SmlExClauseSize;
					}
					// Console.WriteLine("exceptSize = " + exceptSize);
				}
				if (initLocals) headerFlags |= InitLocals;
				if ((offset % 4) != 0) { paddingNeeded = 4 - (offset % 4); }
				codeSize += FatSize;
			}
			// Console.WriteLine("codeSize = " + codeSize + "  headerFlags = " + 
			//                   Hex.Short(headerFlags));
		}

		internal void Write(FileImage output) 
		{
			// Console.WriteLine("Writing header flags = " + Hex.Short(headerFlags));
			if (tinyFormat) {
				// Console.WriteLine("Writing tiny code");
				output.Write((byte)headerFlags);
			} else {
				// Console.WriteLine("Writing fat code");
				output.Write(headerFlags);
				output.Write((ushort)maxStack);
				output.Write(offset);
				output.Write(localSigIx);
			}
			// Console.WriteLine(Hex.Int(tide) + " CIL instructions");
			// Console.WriteLine("starting instructions at " + output.Seek(0,SeekOrigin.Current));
			for (int i=0; i < tide; i++) {
				buffer[i].Write(output);
			}
			// Console.WriteLine("ending instructions at " + output.Seek(0,SeekOrigin.Current));
			for (int i=0; i < paddingNeeded; i++) { output.Write((byte)0); }
			if (exceptions != null) {
				// Console.WriteLine("Writing exceptions");
				// Console.WriteLine("header = " + Hex.Short(exceptHeader) + " exceptSize = " + Hex.Int(exceptSize));
				output.Write(exceptHeader);
				output.Write3Bytes((uint)exceptSize);
				for (int i=0; i < exceptions.Count; i++) {
					TryBlock tryBlock = (TryBlock)exceptions[i];
					tryBlock.Write(output,fatExceptionFormat);
				}
			}
		}

	}

	/**************************************************************************/  
	public abstract class CodeBlock {

		private static readonly int maxCodeSize = 255;
		protected CILLabel start, end;
		protected bool small = true;

		public CodeBlock(CILLabel start, CILLabel end) 
		{
			this.start = start;
			this.end = end;
		}

		internal virtual bool isFat() 
		{
			// Console.WriteLine("block start = " + start.GetLabelOffset() +
			//                  "  block end = " + end.GetLabelOffset());
			return (end.GetLabelOffset() - start.GetLabelOffset()) > maxCodeSize;
		}

		internal virtual void Write(FileImage output, bool fatFormat) 
		{
			if (fatFormat) output.Write(start.GetLabelOffset());
			else output.Write((short)start.GetLabelOffset());
			uint len = end.GetLabelOffset() - start.GetLabelOffset();
			if (fatFormat) output.Write(len);
			else output.Write((byte)len);
		}

	}

	/// <summary>
	/// The descriptor for a guarded block (.try)
	/// </summary>
	public class TryBlock : CodeBlock {
		protected bool fatFormat = false;
		protected int flags = 0;
		ArrayList handlers = new ArrayList();

		/// <summary>
		/// Create a new try block
		/// </summary>
		/// <param name="start">start label for the try block</param>
		/// <param name="end">end label for the try block</param>
		public TryBlock(CILLabel start, CILLabel end) : base(start,end) { }

		/// <summary>
		/// Add a handler to this try block
		/// </summary>
		/// <param name="handler">a handler to be added to the try block</param>
		public void AddHandler(HandlerBlock handler) 
		{
			flags = handler.GetFlag();
			handlers.Add(handler);
		}

		internal void SetSize() 
		{
			fatFormat = base.isFat();
			if (fatFormat) return;
			for (int i=0; i < handlers.Count; i++) {
				HandlerBlock handler = (HandlerBlock)handlers[i];
				if (handler.isFat()) {
					fatFormat = true;
					return;
				}
			}
		}

		internal int NumHandlers() 
		{
			return handlers.Count;
		}

		internal override bool isFat() 
		{
			return fatFormat;
		}

		//Hackish
		internal void ResolveCatchBlocks (MetaData md)
		{
			for (int i=0; i < handlers.Count; i++) {
				Catch c = handlers [i] as Catch;
				if (c != null)
					c.ResolveType (md);
			}
		}

		internal override void Write(FileImage output, bool fatFormat) 
		{
			// Console.WriteLine("writing exception details");
			for (int i=0; i < handlers.Count; i++) {
				// Console.WriteLine("Except block " + i);
				HandlerBlock handler = (HandlerBlock)handlers[i];
				if (fatFormat) output.Write(flags);
				else output.Write((short)flags);
				// Console.WriteLine("flags = " + Hex.Short(flags));
				base.Write(output,fatFormat);
				handler.Write(output,fatFormat);
			}
		}
	}

	public abstract class HandlerBlock : CodeBlock  {

		protected static readonly short ExceptionFlag = 0;
		protected static readonly short FilterFlag = 0x01;
		protected static readonly short FinallyFlag = 0x02;
		protected static readonly short FaultFlag = 0x04;

		public HandlerBlock(CILLabel start, CILLabel end) : base(start,end) { }

		internal virtual short GetFlag() { return ExceptionFlag; }

		internal override void Write(FileImage output, bool fatFormat) 
		{
			base.Write(output,fatFormat);
		}

	}

	/// <summary>
	/// The descriptor for a catch clause (.catch)
	/// </summary>
	public class Catch : HandlerBlock  {

		MetaDataElement exceptType;

		/// <summary>
		/// Create a new catch clause
		/// </summary>
		/// <param name="except">the exception to be caught</param>
		/// <param name="handlerStart">start of the handler code</param>
		/// <param name="handlerEnd">end of the handler code</param>
		public Catch(Class except, CILLabel handlerStart, CILLabel handlerEnd)
			: base(handlerStart, handlerEnd)
		{
			exceptType = except;
		}

		public Catch(Type except, CILLabel handlerStart, CILLabel handlerEnd)
			: base(handlerStart,handlerEnd) 
		{
			exceptType = except;
		}

		internal void ResolveType (MetaData md)
		{
		       exceptType = ((Type) exceptType).GetTypeSpec (md);
		}

		internal override void Write(FileImage output, bool fatFormat) 
		{
			base.Write(output,fatFormat);
			output.Write(exceptType.Token());
		}
	}

	/// <summary>
	/// The descriptor for a filter clause (.filter)
	/// </summary>
	public class Filter : HandlerBlock  {

		CILLabel filterLabel;

		/// <summary>
		/// Create a new filter clause
		/// </summary>
		/// <param name="filterLabel">the label where the filter code starts</param>
		/// <param name="handlerStart">the start of the handler code</param>
		/// <param name="handlerEnd">the end of the handler code</param>
		public Filter(CILLabel filterLabel, CILLabel handlerStart, 
				CILLabel handlerEnd) : base(handlerStart,handlerEnd) 
				{
			this.filterLabel = filterLabel;
		}

		internal override short GetFlag() 
		{
			return FilterFlag; 
		}

		internal override void Write(FileImage output, bool fatFormat) 
		{
			base.Write(output,fatFormat);
			output.Write(filterLabel.GetLabelOffset());
		}

	}

	/// <summary>
	/// Descriptor for a finally block (.finally)
	/// </summary>
	public class Finally : HandlerBlock  {

		/// <summary>
		/// Create a new finally clause
		/// </summary>
		/// <param name="finallyStart">start of finally code</param>
		/// <param name="finallyEnd">end of finally code</param>
		public Finally(CILLabel finallyStart, CILLabel finallyEnd)
			: base(finallyStart,finallyEnd) { }

		internal override short GetFlag() 
		{
			return FinallyFlag; 
		}

		internal override void Write(FileImage output, bool fatFormat) 
		{
			base.Write(output,fatFormat);
			output.Write((int)0);
		}

	}

	/// <summary>
	/// Descriptor for a fault block (.fault)
	/// </summary>
	public class Fault : HandlerBlock  {

		/// <summary>
		/// Create a new fault clause
		/// </summary>
		/// <param name="faultStart">start of the fault code</param>
		/// <param name="faultEnd">end of the fault code</param>
		public Fault(CILLabel faultStart, CILLabel faultEnd)
			: base(faultStart,faultEnd) { }

		internal override short GetFlag() 
		{
			return FaultFlag; 
		}

		internal override void Write(FileImage output, bool fatFormat) 
		{
			base.Write(output,fatFormat);
			output.Write((int)0);

		}
	}

	/**************************************************************************/  
	/// <summary>
	/// Descriptor for the locals for a method
	/// </summary>
	public class LocalSig : Signature {

		private static readonly byte LocalSigByte = 0x7;
		Local[] locals;

		public LocalSig(Local[] locals)         
		{
			this.locals = locals;
			tabIx = MDTable.StandAloneSig;
		}

		internal sealed override void BuildTables(MetaData md) 
		{
			if (done) return;
			MemoryStream sig = new MemoryStream();
			sig.WriteByte(LocalSigByte);
			MetaData.CompressNum((uint)locals.Length,sig);
			for (int i=0; i < locals.Length; i++) {
				((Local)locals[i]).TypeSig(sig);
			}
			sigIx = md.AddToBlobHeap(sig.ToArray());
			done = true;
		}

	}

	/**************************************************************************/  
	/// <summary>
	/// Signature for calli instruction
	/// </summary>
	public class CalliSig : Signature {

		private static readonly byte Sentinel = 0x41;
		CallConv callConv;
		Type returnType;
		Type[] parameters, optParams;
		uint numPars = 0, numOptPars = 0;

		/// <summary>
		/// Create a signature for a calli instruction
		/// </summary>
		/// <param name="cconv">calling conventions</param>
		/// <param name="retType">return type</param>
		/// <param name="pars">parameter types</param>
		public CalliSig(CallConv cconv, Type retType, Type[] pars) 
		{
			tabIx = MDTable.StandAloneSig;
			callConv = cconv;
			returnType = retType;
			parameters = pars;
			if (pars != null) numPars = (uint)pars.Length;
		}

		/// <summary>
		/// Add the optional parameters to a vararg method
		/// This method sets the vararg calling convention
		/// </summary>
		/// <param name="optPars">the optional pars for the vararg call</param>
		public void AddVarArgs(Type[] optPars) 
		{
			optParams = optPars;
			if (optPars != null) numOptPars = (uint)optPars.Length;
			callConv |= CallConv.Vararg;
		}

		/// <summary>
		/// Add extra calling conventions to this callsite signature
		/// </summary>
		/// <param name="cconv"></param>
		public void AddCallingConv(CallConv cconv) 
		{
			callConv |= cconv;
		}

		internal sealed override void BuildTables(MetaData md) 
		{
			if (done) return;
			MemoryStream sig = new MemoryStream();
			sig.WriteByte((byte)callConv);
			MetaData.CompressNum(numPars+numOptPars,sig);
			returnType.TypeSig(sig);
			for (int i=0; i < numPars; i++) {
				parameters[i].TypeSig(sig);
			}
			sigIx = md.AddToBlobHeap(sig.ToArray());
			if (numOptPars > 0) {
				sig.WriteByte(Sentinel);
				for (int i=0; i < numOptPars; i++) {
					optParams[i].TypeSig(sig);
				}
			}
			done = true;
		}

	}

	/**************************************************************************/  
	/// <summary>
	/// Descriptor for a local of a method
	/// </summary>
	public class Local {

		private static readonly byte Pinned = 0x45;
		string name;
		Type type;
		bool pinned = false, byref = false;

		/// <summary>
		/// Create a new local variable 
		/// </summary>
		/// <param name="lName">name of the local variable</param>
		/// <param name="lType">type of the local variable</param>
		public Local(string lName, Type lType) 
		{
			name = lName;
			type = lType;
		}

		/// <summary>
		/// Create a new local variable that is byref and/or pinned
		/// </summary>
		/// <param name="lName">local name</param>
		/// <param name="lType">local type</param>
		/// <param name="byRef">is byref</param>
		/// <param name="isPinned">has pinned attribute</param>
		public Local(string lName, Type lType, bool byRef, bool isPinned)
		{
			name = lName;
			type = lType;
			byref = byRef;
			pinned = isPinned;
		}

		internal void TypeSig(MemoryStream str) 
		{
			if (pinned) str.WriteByte(Pinned);
			type.TypeSig(str);
		}

	}

	/**************************************************************************/  
	/// <summary>
	/// A label in the IL
	/// </summary>
	public class CILLabel {

		CILInstruction branch;
		CILInstruction[] multipleBranches;
		int tide = 0;
		CILInstruction labInstr;
		uint offset = 0;
		bool absolute;


		public CILLabel (uint offset, bool absolute) 
		{
			this.offset = offset;
			this.absolute = absolute;
		}

		public CILLabel (uint offset) : this (offset, false)
		{
		}


		internal CILLabel() 
		{
		}

		internal void AddBranch(CILInstruction instr) 
		{
			if (branch == null) {
				branch = instr;
				return;
			}
			if (multipleBranches == null) {
				multipleBranches = new CILInstruction[2];
			} else if (tide >= multipleBranches.Length) {
				CILInstruction[] tmp = multipleBranches;
				multipleBranches = new CILInstruction[tmp.Length*2];
				for (int i=0; i < tide; i++) {
					multipleBranches[i] = tmp[i];
				}
			}
			multipleBranches[tide++] = instr;
		}

		internal void AddLabelInstr(LabelInstr lInstr) 
		{
			labInstr = lInstr;
		}

		internal uint GetLabelOffset() 
		{
			if (absolute) return offset;
			if (labInstr == null) return 0;
			return labInstr.offset + offset;
		}

	}


}