context: /*
   * This code is derived from rhino (http://github.com/mozilla/rhino)
   * 
   * This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/.
   */
  
  using System;
  using System.IO;
  using System.Reflection;
  using System.Reflection.Emit;
  using System.Runtime.Serialization;
  using System.Text;
  using Rhino;
  using Sharpen;
  
  namespace Org.Mozilla.Classfile
  {
  	/// <summary>
  	/// ClassFileWriter
  	/// A ClassFileWriter is used to write a Java class file.
  	/// </summary>
  	/// <remarks>
  	/// ClassFileWriter
  	/// A ClassFileWriter is used to write a Java class file. Methods are
  	/// provided to create fields and methods, and within methods to write
  	/// Java bytecodes.
  	/// </remarks>
  	/// <author>Roger Lawrence</author>
  	public class ClassFileWriter
  	{
  		/// <summary>
  		/// Thrown for cases where the error in generating the class file is
  		/// due to a program size constraints rather than a likely bug in the
  		/// compiler.
  		/// </summary>
  		/// <remarks>
  		/// Thrown for cases where the error in generating the class file is
  		/// due to a program size constraints rather than a likely bug in the
  		/// compiler.
  		/// </remarks>
  		[Serializable]
  		public class ClassFileFormatException : Exception
  		{
  			public ClassFileFormatException()
  			{
  			}
  
  			public ClassFileFormatException(string message) : base(message)
  			{
  			}
  
  			public ClassFileFormatException(string message, Exception inner) : base(message, inner)
  			{
  			}
  
  			protected ClassFileFormatException(SerializationInfo info, StreamingContext context) : base(info, context)
  			{
  			}
  		}
  
  		/// <summary>Construct a ClassFileWriter for a class.</summary>
  		/// <remarks>Construct a ClassFileWriter for a class.</remarks>
  		/// <param name="className">
  		/// the name of the class to write, including
  		/// full package qualification.
  		/// </param>
  		/// <param name="superClassName">
  		/// the name of the superclass of the class
  		/// to write, including full package qualification.
  		/// </param>
  		/// <param name="sourceFileName">
  		/// the name of the source file to use for
  		/// producing debug information, or null if debug information
  		/// is not desired
  		/// </param>
  		public ClassFileWriter(string className, string superClassName, string sourceFileName)
  		{
  			generatedClassName = className;
  			itsConstantPool = new ConstantPool(this);
  			itsThisClassIndex = itsConstantPool.AddClass(className);
  			itsSuperClassIndex = itsConstantPool.AddClass(superClassName);
  			if (sourceFileName != null)
  			{
  				itsSourceFileNameIndex = itsConstantPool.AddUtf8(sourceFileName);
  			}
  			// All "new" implementations are supposed to output ACC_SUPER as a
  			// class flag. This is specified in the first JVM spec, so it should
  			// be old enough that it's okay to always set it.
  			itsFlags = ACC_PUBLIC | ACC_SUPER;
  		}
  
  		public string GetClassName()
  		{
  			return generatedClassName;
  		}
  
  		/// <summary>Add an interface implemented by this class.</summary>
  		/// <remarks>
  		/// Add an interface implemented by this class.
  		/// This method may be called multiple times for classes that
  		/// implement multiple interfaces.
  		/// </remarks>
  		/// <param name="interfaceName">
  		/// a name of an interface implemented
  		/// by the class being written, including full package
  		/// qualification.
  		/// </param>
  		public virtual void AddInterface(string interfaceName)
  		{
  			short interfaceIndex = itsConstantPool.AddClass(interfaceName);
  			itsInterfaces.Add(Sharpen.Extensions.ValueOf(interfaceIndex));
  		}
  
  		public const short ACC_PUBLIC = unchecked((int)(0x0001));
  
  		public const short ACC_PRIVATE = unchecked((int)(0x0002));
  
  		public const short ACC_PROTECTED = unchecked((int)(0x0004));
  
  		public const short ACC_STATIC = unchecked((int)(0x0008));
  
  		public const short ACC_FINAL = unchecked((int)(0x0010));
  
  		public const short ACC_SUPER = unchecked((int)(0x0020));
  
  		public const short ACC_SYNCHRONIZED = unchecked((int)(0x0020));
  
  		public const short ACC_VOLATILE = unchecked((int)(0x0040));
  
  		public const short ACC_TRANSIENT = unchecked((int)(0x0080));
  
  		public const short ACC_NATIVE = unchecked((int)(0x0100));
  
  		public const short ACC_ABSTRACT = unchecked((int)(0x0400));
  
  		/// <summary>Set the class's flags.</summary>
  		/// <remarks>
  		/// Set the class's flags.
  		/// Flags must be a set of the following flags, bitwise or'd
  		/// together:
  		/// ACC_PUBLIC
  		/// ACC_PRIVATE
  		/// ACC_PROTECTED
  		/// ACC_FINAL
  		/// ACC_ABSTRACT
  		/// TODO: check that this is the appropriate set
  		/// </remarks>
  		/// <param name="flags">the set of class flags to set</param>
  		public virtual void SetFlags(short flags)
  		{
  			itsFlags = flags;
  		}
  
  		internal static string GetSlashedForm(string name)
  		{
  			return name.Replace('.', '/');
  		}
  
  		/// <summary>
  		/// Convert Java class name in dot notation into
  		/// "Lname-with-dots-replaced-by-slashes;" form suitable for use as
  		/// JVM type signatures.
  		/// </summary>
  		/// <remarks>
  		/// Convert Java class name in dot notation into
  		/// "Lname-with-dots-replaced-by-slashes;" form suitable for use as
  		/// JVM type signatures.
  		/// </remarks>
  		public static string ClassNameToSignature(string name)
  		{
  			int nameLength = name.Length;
  			int colonPos = 1 + nameLength;
  			char[] buf = new char[colonPos + 1];
  			buf[0] = 'L';
  			buf[colonPos] = ';';
  			Sharpen.Runtime.GetCharsForString(name, 0, nameLength, buf, 1);
  			for (int i = 1; i != colonPos; ++i)
  			{
  				if (buf[i] == '.')
  				{
  					buf[i] = '/';
  				}
  			}
  			return new string(buf, 0, colonPos + 1);
  		}
  
  		/// <summary>Add a field to the class.</summary>
  		/// <remarks>Add a field to the class.</remarks>
  		/// <param name="fieldName">the name of the field</param>
  		/// <param name="type">the type of the field using ...</param>
  		/// <param name="flags">
  		/// the attributes of the field, such as ACC_PUBLIC, etc.
  		/// bitwise or'd together
  		/// </param>
  		public virtual void AddField(string fieldName, string type, short flags)
  		{
  			var field = typeBuilder.DefineField(fieldName, Type.GetType(type), (FieldAttributes) flags);
  			itsFields.Add(field);
  		}
  
  		private TypeBuilder typeBuilder;
  
  		/// <summary>Add a field to the class.</summary>
  		/// <remarks>Add a field to the class.</remarks>
  		/// <param name="fieldName">the name of the field</param>
  		/// <param name="type">the type of the field using ...</param>
  		/// <param name="flags">
  		/// the attributes of the field, such as ACC_PUBLIC, etc.
  		/// bitwise or'd together
  		/// </param>
  		/// <param name="value">an initial integral value</param>
  		public virtual void AddField(string fieldName, string type, short flags, int value)
  		{
  			short fieldNameIndex = itsConstantPool.AddUtf8(fieldName);
  			short typeIndex = itsConstantPool.AddUtf8(type);
  			ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, flags);
  			field.SetAttributes(itsConstantPool.AddUtf8("ConstantValue"), (short)0, (short)0, itsConstantPool.AddConstant(value));
  			itsFields.Add(field);
  		}
  
  		/// <summary>Add a field to the class.</summary>
  		/// <remarks>Add a field to the class.</remarks>
  		/// <param name="fieldName">the name of the field</param>
  		/// <param name="type">the type of the field using ...</param>
  		/// <param name="flags">
  		/// the attributes of the field, such as ACC_PUBLIC, etc.
  		/// bitwise or'd together
  		/// </param>
  		/// <param name="value">an initial long value</param>
  		public virtual void AddField(string fieldName, string type, short flags, long value)
  		{
  			short fieldNameIndex = itsConstantPool.AddUtf8(fieldName);
  			short typeIndex = itsConstantPool.AddUtf8(type);
  			ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, flags);
  			field.SetAttributes(itsConstantPool.AddUtf8("ConstantValue"), (short)0, (short)2, itsConstantPool.AddConstant(value));
  			itsFields.Add(field);
  		}
  
  		/// <summary>Add a field to the class.</summary>
  		/// <remarks>Add a field to the class.</remarks>
  		/// <param name="fieldName">the name of the field</param>
  		/// <param name="type">the type of the field using ...</param>
  		/// <param name="flags">
  		/// the attributes of the field, such as ACC_PUBLIC, etc.
  		/// bitwise or'd together
  		/// </param>
  		/// <param name="value">an initial double value</param>
  		public virtual void AddField(string fieldName, string type, short flags, double value)
  		{
  			short fieldNameIndex = itsConstantPool.AddUtf8(fieldName);
  			short typeIndex = itsConstantPool.AddUtf8(type);
  			ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, flags);
  			field.SetAttributes(itsConstantPool.AddUtf8("ConstantValue"), (short)0, (short)2, itsConstantPool.AddConstant(value));
  			itsFields.Add(field);
  		}
  
  		/// <summary>
  		/// Add Information about java variable to use when generating the local
  		/// variable table.
  		/// </summary>
  		/// <remarks>
  		/// Add Information about java variable to use when generating the local
  		/// variable table.
  		/// </remarks>
  		/// <param name="name">variable name.</param>
  		/// <param name="type">variable type as bytecode descriptor string.</param>
  		/// <param name="startPC">
  		/// the starting bytecode PC where this variable is live,
  		/// or -1 if it does not have a Java register.
  		/// </param>
  		/// <param name="register">
  		/// the Java register number of variable
  		/// or -1 if it does not have a Java register.
  		/// </param>
  		public virtual void AddVariableDescriptor(string name, string type, int startPC, int register)
  		{
  			int nameIndex = itsConstantPool.AddUtf8(name);
  			int descriptorIndex = itsConstantPool.AddUtf8(type);
  			int[] chunk = new int[] { nameIndex, descriptorIndex, startPC, register };
  			if (itsVarDescriptors == null)
  			{
  				itsVarDescriptors = new ObjArray();
  			}
  			itsVarDescriptors.Add(chunk);
  		}
  
  		/// <summary>Add a method and begin adding code.</summary>
  		/// <remarks>
  		/// Add a method and begin adding code.
  		/// This method must be called before other methods for adding code,
  		/// exception tables, etc. can be invoked.
  		/// </remarks>
  		/// <param name="methodName">the name of the method</param>
  		/// <param name="type">a string representing the type</param>
  		/// <param name="flags">
  		/// the attributes of the field, such as ACC_PUBLIC, etc.
  		/// bitwise or'd together
  		/// </param>
  		public virtual void StartMethod(string methodName, string type, short flags)
  		{
  			short methodNameIndex = itsConstantPool.AddUtf8(methodName);
  			short typeIndex = itsConstantPool.AddUtf8(type);
  			itsCurrentMethod = new ClassFileMethod(methodName, methodNameIndex, type, typeIndex, flags);
  			itsJumpFroms = new UintMap();
  			itsMethods.Add(itsCurrentMethod);
  			AddSuperBlockStart(0);
  		}
  
  		/// <summary>Complete generation of the method.</summary>
  		/// <remarks>
  		/// Complete generation of the method.
  		/// After this method is called, no more code can be added to the
  		/// method begun with <code>startMethod</code>.
  		/// </remarks>
  		/// <param name="maxLocals">
  		/// the maximum number of local variable slots
  		/// (a.k.a. Java registers) used by the method
  		/// </param>
  		public virtual void StopMethod(short maxLocals)
  		{
  			if (itsCurrentMethod == null)
  			{
  				throw new InvalidOperationException("No method to stop");
  			}
  			FixLabelGotos();
  			itsMaxLocals = maxLocals;
  			ClassFileWriter.StackMapTable stackMap = null;
  			if (GenerateStackMap)
  			{
  				FinalizeSuperBlockStarts();
  				stackMap = new ClassFileWriter.StackMapTable(this);
  				stackMap.Generate();
  			}
  			int lineNumberTableLength = 0;
  			if (itsLineNumberTable != null)
  			{
  				// 6 bytes for the attribute header
  				// 2 bytes for the line number count
  				// 4 bytes for each entry
  				lineNumberTableLength = 6 + 2 + (itsLineNumberTableTop * 4);
  			}
  			int variableTableLength = 0;
  			if (itsVarDescriptors != null)
  			{
  				// 6 bytes for the attribute header
  				// 2 bytes for the variable count
  				// 10 bytes for each entry
  				variableTableLength = 6 + 2 + (itsVarDescriptors.Size() * 10);
  			}
  			int stackMapTableLength = 0;
  			if (stackMap != null)
  			{
  				int stackMapWriteSize = stackMap.ComputeWriteSize();
  				if (stackMapWriteSize > 0)
  				{
  					stackMapTableLength = 6 + stackMapWriteSize;
  				}
  			}
  			int attrLength = 2 + 4 + 2 + 2 + 4 + itsCodeBufferTop + 2 + (itsExceptionTableTop * 8) + 2 + lineNumberTableLength + variableTableLength + stackMapTableLength;
  			// attribute_name_index
  			// attribute_length
  			// max_stack
  			// max_locals
  			// code_length
  			// exception_table_length
  			// attributes_count
  			if (attrLength > 65536)
  			{
  				// See http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html,
  				// section 4.10, "The amount of code per non-native, non-abstract
  				// method is limited to 65536 bytes...
  				throw new ClassFileWriter.ClassFileFormatException("generated bytecode for method exceeds 64K limit.");
  			}
  			byte[] codeAttribute = new byte[attrLength];
  			int index = 0;
  			int codeAttrIndex = itsConstantPool.AddUtf8("Code");
  			index = PutInt16(codeAttrIndex, codeAttribute, index);
  			attrLength -= 6;
  			// discount the attribute header
  			index = PutInt32(attrLength, codeAttribute, index);
  			index = PutInt16(itsMaxStack, codeAttribute, index);
  			index = PutInt16(itsMaxLocals, codeAttribute, index);
  			index = PutInt32(itsCodeBufferTop, codeAttribute, index);
  			System.Array.Copy(itsCodeBuffer, 0, codeAttribute, index, itsCodeBufferTop);
  			index += itsCodeBufferTop;
  			if (itsExceptionTableTop > 0)
  			{
  				index = PutInt16(itsExceptionTableTop, codeAttribute, index);
  				for (int i = 0; i < itsExceptionTableTop; i++)
  				{
  					ExceptionTableEntry ete = itsExceptionTable[i];
  					short startPC = (short)GetLabelPC(ete.itsStartLabel);
  					short endPC = (short)GetLabelPC(ete.itsEndLabel);
  					short handlerPC = (short)GetLabelPC(ete.itsHandlerLabel);
  					short catchType = ete.itsCatchType;
  					if (startPC == -1)
  					{
  						throw new InvalidOperationException("start label not defined");
  					}
  					if (endPC == -1)
  					{
  						throw new InvalidOperationException("end label not defined");
  					}
  					if (handlerPC == -1)
  					{
  						throw new InvalidOperationException("handler label not defined");
  					}
  					index = PutInt16(startPC, codeAttribute, index);
  					index = PutInt16(endPC, codeAttribute, index);
  					index = PutInt16(handlerPC, codeAttribute, index);
  					index = PutInt16(catchType, codeAttribute, index);
  				}
  			}
  			else
  			{
  				// write 0 as exception table length
  				index = PutInt16(0, codeAttribute, index);
  			}
  			int attributeCount = 0;
  			if (itsLineNumberTable != null)
  			{
  				attributeCount++;
  			}
  			if (itsVarDescriptors != null)
  			{
  				attributeCount++;
  			}
  			if (stackMapTableLength > 0)
  			{
  				attributeCount++;
  			}
  			index = PutInt16(attributeCount, codeAttribute, index);
  			if (itsLineNumberTable != null)
  			{
  				int lineNumberTableAttrIndex = itsConstantPool.AddUtf8("LineNumberTable");
  				index = PutInt16(lineNumberTableAttrIndex, codeAttribute, index);
  				int tableAttrLength = 2 + (itsLineNumberTableTop * 4);
  				index = PutInt32(tableAttrLength, codeAttribute, index);
  				index = PutInt16(itsLineNumberTableTop, codeAttribute, index);
  				for (int i = 0; i < itsLineNumberTableTop; i++)
  				{
  					index = PutInt32(itsLineNumberTable[i], codeAttribute, index);
  				}
  			}
  			if (itsVarDescriptors != null)
  			{
  				int variableTableAttrIndex = itsConstantPool.AddUtf8("LocalVariableTable");
  				index = PutInt16(variableTableAttrIndex, codeAttribute, index);
  				int varCount = itsVarDescriptors.Size();
  				int tableAttrLength = 2 + (varCount * 10);
  				index = PutInt32(tableAttrLength, codeAttribute, index);
  				index = PutInt16(varCount, codeAttribute, index);
  				for (int i = 0; i < varCount; i++)
  				{
  					int[] chunk = (int[])itsVarDescriptors.Get(i);
  					int nameIndex = chunk[0];
  					int descriptorIndex = chunk[1];
  					int startPC = chunk[2];
  					int register = chunk[3];
  					int length = itsCodeBufferTop - startPC;
  					index = PutInt16(startPC, codeAttribute, index);
  					index = PutInt16(length, codeAttribute, index);
  					index = PutInt16(nameIndex, codeAttribute, index);
  					index = PutInt16(descriptorIndex, codeAttribute, index);
  					index = PutInt16(register, codeAttribute, index);
  				}
  			}
  			if (stackMapTableLength > 0)
  			{
  				int stackMapTableAttrIndex = itsConstantPool.AddUtf8("StackMapTable");
  				int start = index;
  				index = PutInt16(stackMapTableAttrIndex, codeAttribute, index);
  				index = stackMap.Write(codeAttribute, index);
  			}
  			itsCurrentMethod.SetCodeAttribute(codeAttribute);
  			itsExceptionTable = null;
  			itsExceptionTableTop = 0;
  			itsLineNumberTableTop = 0;
  			itsCodeBufferTop = 0;
  			itsCurrentMethod = null;
  			itsMaxStack = 0;
  			itsStackTop = 0;
  			itsLabelTableTop = 0;
  			itsFixupTableTop = 0;
  			itsVarDescriptors = null;
  			itsSuperBlockStarts = null;
  			itsSuperBlockStartsTop = 0;
  			itsJumpFroms = null;
  		}
  
  		/// <summary>Add the single-byte opcode to the current method.</summary>
  		/// <remarks>Add the single-byte opcode to the current method.</remarks>
  		/// <param name="theOpCode">the opcode of the bytecode</param>
  		public virtual void Add(int theOpCode)
  		{
  			if (OpcodeCount(theOpCode) != 0)
  			{
  				throw new ArgumentException("Unexpected operands");
  			}
  			int newStack = itsStackTop + StackChange(theOpCode);
  			if (newStack < 0 || short.MaxValue < newStack)
  			{
  				BadStack(newStack);
  			}
  			AddToCodeBuffer(theOpCode);
  			itsStackTop = (short)newStack;
  			if (newStack > itsMaxStack)
  			{
  				itsMaxStack = (short)newStack;
  			}
  			if (theOpCode == ByteCode.ATHROW)
  			{
  				AddSuperBlockStart(itsCodeBufferTop);
  			}
  		}
  
  		/// <summary>Add a single-operand opcode to the current method.</summary>
  		/// <remarks>Add a single-operand opcode to the current method.</remarks>
  		/// <param name="theOpCode">the opcode of the bytecode</param>
  		/// <param name="theOperand">the operand of the bytecode</param>
  		public virtual void Add(int theOpCode, int theOperand)
  		{
  			int newStack = itsStackTop + StackChange(theOpCode);
  			if (newStack < 0 || short.MaxValue < newStack)
  			{
  				BadStack(newStack);
  			}
  			switch (theOpCode)
  			{
  				case ByteCode.GOTO:
  				{
  					// This is necessary because dead code is seemingly being
  					// generated and Sun's verifier is expecting type state to be
  					// placed even at dead blocks of code.
  					AddSuperBlockStart(itsCodeBufferTop + 3);
  					goto case ByteCode.IFEQ;
  				}
  
  				case ByteCode.IFEQ:
  				case ByteCode.IFNE:
  				case ByteCode.IFLT:
  				case ByteCode.IFGE:
  				case ByteCode.IFGT:
  				case ByteCode.IFLE:
  				case ByteCode.IF_ICMPEQ:
  				case ByteCode.IF_ICMPNE:
  				case ByteCode.IF_ICMPLT:
  				case ByteCode.IF_ICMPGE:
  				case ByteCode.IF_ICMPGT:
  				case ByteCode.IF_ICMPLE:
  				case ByteCode.IF_ACMPEQ:
  				case ByteCode.IF_ACMPNE:
  				case ByteCode.JSR:
  				case ByteCode.IFNULL:
  				case ByteCode.IFNONNULL:
  				{
  					// fallthru...
  					if ((theOperand & unchecked((int)(0x80000000))) != unchecked((int)(0x80000000)))
  					{
  						if ((theOperand < 0) || (theOperand > 65535))
  						{
  							throw new ArgumentException("Bad label for branch");
  						}
  					}
  					int branchPC = itsCodeBufferTop;
  					AddToCodeBuffer(theOpCode);
  					if ((theOperand & unchecked((int)(0x80000000))) != unchecked((int)(0x80000000)))
  					{
  						// hard displacement
  						AddToCodeInt16(theOperand);
  						int target = theOperand + branchPC;
  						AddSuperBlockStart(target);
  						itsJumpFroms.Put(target, branchPC);
  					}
  					else
  					{
  						// a label
  						int targetPC = GetLabelPC(theOperand);
  						if (targetPC != -1)
  						{
  							int offset = targetPC - branchPC;
  							AddToCodeInt16(offset);
  							AddSuperBlockStart(targetPC);
  							itsJumpFroms.Put(targetPC, branchPC);
  						}
  						else
  						{
  							AddLabelFixup(theOperand, branchPC + 1);
  							AddToCodeInt16(0);
  						}
  					}
  					break;
  				}
  
  				case ByteCode.BIPUSH:
  				{
  					if (unchecked((byte)theOperand) != theOperand)
  					{
  						throw new ArgumentException("out of range byte");
  					}
  					AddToCodeBuffer(theOpCode);
  					AddToCodeBuffer(unchecked((byte)theOperand));
  					break;
  				}
  
  				case ByteCode.SIPUSH:
  				{
  					if ((short)theOperand != theOperand)
  					{
  						throw new ArgumentException("out of range short");
  					}
  					AddToCodeBuffer(theOpCode);
  					AddToCodeInt16(theOperand);
  					break;
  				}
  
  				case ByteCode.NEWARRAY:
  				{
  					if (!(0 <= theOperand && theOperand < 256))
  					{
  						throw new ArgumentException("out of range index");
  					}
  					AddToCodeBuffer(theOpCode);
  					AddToCodeBuffer(theOperand);
  					break;
  				}
  
  				case ByteCode.GETFIELD:
  				case ByteCode.PUTFIELD:
  				{
  					if (!(0 <= theOperand && theOperand < 65536))
  					{
  						throw new ArgumentException("out of range field");
  					}
  					AddToCodeBuffer(theOpCode);
  					AddToCodeInt16(theOperand);
  					break;
  				}
  
  				case ByteCode.LDC:
  				case ByteCode.LDC_W:
  				case ByteCode.LDC2_W:
  				{
  					if (!(0 <= theOperand && theOperand < 65536))
  					{
  						throw new ArgumentException("out of range index");
  					}
  					if (theOperand >= 256 || theOpCode == ByteCode.LDC_W || theOpCode == ByteCode.LDC2_W)
  					{
  						if (theOpCode == ByteCode.LDC)
  						{
  							AddToCodeBuffer(ByteCode.LDC_W);
  						}
  						else
  						{
  							AddToCodeBuffer(theOpCode);
  						}
  						AddToCodeInt16(theOperand);
  					}
  					else
  					{
  						AddToCodeBuffer(theOpCode);
  						AddToCodeBuffer(theOperand);
  					}
  					break;
  				}
  
  				case ByteCode.RET:
  				case ByteCode.ILOAD:
  				case ByteCode.LLOAD:
  				case ByteCode.FLOAD:
  				case ByteCode.DLOAD:
  				case ByteCode.ALOAD:
  				case ByteCode.ISTORE:
  				case ByteCode.LSTORE:
  				case ByteCode.FSTORE:
  				case ByteCode.DSTORE:
  				case ByteCode.ASTORE:
  				{
  					if (!(0 <= theOperand && theOperand < 65536))
  					{
  						throw new ClassFileWriter.ClassFileFormatException("out of range variable");
  					}
  					if (theOperand >= 256)
  					{
  						AddToCodeBuffer(ByteCode.WIDE);
  						AddToCodeBuffer(theOpCode);
  						AddToCodeInt16(theOperand);
  					}
  					else
  					{
  						AddToCodeBuffer(theOpCode);
  						AddToCodeBuffer(theOperand);
  					}
  					break;
  				}
  
  				default:
  				{
  					throw new ArgumentException("Unexpected opcode for 1 operand");
  				}
  			}
  			itsStackTop = (short)newStack;
  			if (newStack > itsMaxStack)
  			{
  				itsMaxStack = (short)newStack;
  			}
  		}
  
  		/// <summary>Generate the load constant bytecode for the given integer.</summary>
  		/// <remarks>Generate the load constant bytecode for the given integer.</remarks>
  		/// <param name="k">the constant</param>
  		public virtual void AddLoadConstant(int k)
  		{
  			switch (k)
  			{
  				case 0:
  				{
  					Add(ByteCode.ICONST_0);
  					break;
  				}
  
  				case 1:
  				{
  					Add(ByteCode.ICONST_1);
  					break;
  				}
  
  				case 2:
  				{
  					Add(ByteCode.ICONST_2);
  					break;
  				}
  
  				case 3:
  				{
  					Add(ByteCode.ICONST_3);
  					break;
  				}
  
  				case 4:
  				{
  					Add(ByteCode.ICONST_4);
  					break;
  				}
  
  				case 5:
  				{
  					Add(ByteCode.ICONST_5);
  					break;
  				}
  
  				default:
  				{
  					Add(ByteCode.LDC, itsConstantPool.AddConstant(k));
  					break;
  				}
  			}
  		}
  
  		/// <summary>Generate the load constant bytecode for the given long.</summary>
  		/// <remarks>Generate the load constant bytecode for the given long.</remarks>
  		/// <param name="k">the constant</param>
  		public virtual void AddLoadConstant(long k)
  		{
  			Add(ByteCode.LDC2_W, itsConstantPool.AddConstant(k));
  		}
  
  		/// <summary>Generate the load constant bytecode for the given float.</summary>
  		/// <remarks>Generate the load constant bytecode for the given float.</remarks>
  		/// <param name="k">the constant</param>
  		public virtual void AddLoadConstant(float k)
  		{
  			Add(ByteCode.LDC, itsConstantPool.AddConstant(k));
  		}
  
  		/// <summary>Generate the load constant bytecode for the given double.</summary>
  		/// <remarks>Generate the load constant bytecode for the given double.</remarks>
  		/// <param name="k">the constant</param>
  		public virtual void AddLoadConstant(double k)
  		{
  			Add(ByteCode.LDC2_W, itsConstantPool.AddConstant(k));
  		}
  
  		/// <summary>Generate the load constant bytecode for the given string.</summary>
  		/// <remarks>Generate the load constant bytecode for the given string.</remarks>
  		/// <param name="k">the constant</param>
  		public virtual void AddLoadConstant(string k)
  		{
  			Add(ByteCode.LDC, itsConstantPool.AddConstant(k));
  		}
  
  		/// <summary>Add the given two-operand bytecode to the current method.</summary>
  		/// <remarks>Add the given two-operand bytecode to the current method.</remarks>
  		/// <param name="theOpCode">the opcode of the bytecode</param>
  		/// <param name="theOperand1">the first operand of the bytecode</param>
  		/// <param name="theOperand2">the second operand of the bytecode</param>
  		public virtual void Add(int theOpCode, int theOperand1, int theOperand2)
  		{
  			int newStack = itsStackTop + StackChange(theOpCode);
  			if (newStack < 0 || short.MaxValue < newStack)
  			{
  				BadStack(newStack);
  			}
  			if (theOpCode == ByteCode.IINC)
  			{
  				if (!(0 <= theOperand1 && theOperand1 < 65536))
  				{
  					throw new ClassFileWriter.ClassFileFormatException("out of range variable");
  				}
  				if (!(0 <= theOperand2 && theOperand2 < 65536))
  				{
  					throw new ClassFileWriter.ClassFileFormatException("out of range increment");
  				}
  				if (theOperand1 > 255 || theOperand2 < -128 || theOperand2 > 127)
  				{
  					AddToCodeBuffer(ByteCode.WIDE);
  					AddToCodeBuffer(ByteCode.IINC);
  					AddToCodeInt16(theOperand1);
  					AddToCodeInt16(theOperand2);
  				}
  				else
  				{
  					AddToCodeBuffer(ByteCode.IINC);
  					AddToCodeBuffer(theOperand1);
  					AddToCodeBuffer(theOperand2);
  				}
  			}
  			else
  			{
  				if (theOpCode == ByteCode.MULTIANEWARRAY)
  				{
  					if (!(0 <= theOperand1 && theOperand1 < 65536))
  					{
  						throw new ArgumentException("out of range index");
  					}
  					if (!(0 <= theOperand2 && theOperand2 < 256))
  					{
  						throw new ArgumentException("out of range dimensions");
  					}
  					AddToCodeBuffer(ByteCode.MULTIANEWARRAY);
  					AddToCodeInt16(theOperand1);
  					AddToCodeBuffer(theOperand2);
  				}
  				else
  				{
  					throw new ArgumentException("Unexpected opcode for 2 operands");
  				}
  			}
  			itsStackTop = (short)newStack;
  			if (newStack > itsMaxStack)
  			{
  				itsMaxStack = (short)newStack;
  			}
  		}
  
  		public virtual void Add(int theOpCode, string className)
  		{
  			int newStack = itsStackTop + StackChange(theOpCode);
  			if (newStack < 0 || short.MaxValue < newStack)
  			{
  				BadStack(newStack);
  			}
  			switch (theOpCode)
  			{
  				case ByteCode.NEW:
  				case ByteCode.ANEWARRAY:
  				case ByteCode.CHECKCAST:
  				case ByteCode.INSTANCEOF:
  				{
  					short classIndex = itsConstantPool.AddClass(className);
  					AddToCodeBuffer(theOpCode);
  					AddToCodeInt16(classIndex);
  					break;
  				}
  
  				default:
  				{
  					throw new ArgumentException("bad opcode for class reference");
  				}
  			}
  			itsStackTop = (short)newStack;
  			if (newStack > itsMaxStack)
  			{
  				itsMaxStack = (short)newStack;
  			}
  		}
  
  		public virtual void Add(int theOpCode, string className, string fieldName, string fieldType)
  		{
  			int newStack = itsStackTop + StackChange(theOpCode);
  			char fieldTypeChar = fieldType[0];
  			int fieldSize = (fieldTypeChar == 'J' || fieldTypeChar == 'D') ? 2 : 1;
  			switch (theOpCode)
  			{
  				case ByteCode.GETFIELD:
  				case ByteCode.GETSTATIC:
  				{
  					newStack += fieldSize;
  					break;
  				}
  
  				case ByteCode.PUTSTATIC:
  				case ByteCode.PUTFIELD:
  				{
  					newStack -= fieldSize;
  					break;
  				}
  
  				default:
  				{
  					throw new ArgumentException("bad opcode for field reference");
  				}
  			}
  			if (newStack < 0 || short.MaxValue < newStack)
  			{
  				BadStack(newStack);
  			}
  			short fieldRefIndex = itsConstantPool.AddFieldRef(className, fieldName, fieldType);
  			AddToCodeBuffer(theOpCode);
  			AddToCodeInt16(fieldRefIndex);
  			itsStackTop = (short)newStack;
  			if (newStack > itsMaxStack)
  			{
  				itsMaxStack = (short)newStack;
  			}
  		}
  
  		public virtual void AddInvoke(int theOpCode, string className, string methodName, string methodType)
  		{
  			int parameterInfo = SizeOfParameters(methodType);
  			int parameterCount = (int)(((uint)parameterInfo) >> 16);
  			int stackDiff = (short)parameterInfo;
  			int newStack = itsStackTop + stackDiff;
  			newStack += StackChange(theOpCode);
  			// adjusts for 'this'
  			if (newStack < 0 || short.MaxValue < newStack)
  			{
  				BadStack(newStack);
  			}
  			switch (theOpCode)
  			{
  				case ByteCode.INVOKEVIRTUAL:
  				case ByteCode.INVOKESPECIAL:
  				case ByteCode.INVOKESTATIC:
  				case ByteCode.INVOKEINTERFACE:
  				{
  					AddToCodeBuffer(theOpCode);
  					if (theOpCode == ByteCode.INVOKEINTERFACE)
  					{
  						short ifMethodRefIndex = itsConstantPool.AddInterfaceMethodRef(className, methodName, methodType);
  						AddToCodeInt16(ifMethodRefIndex);
  						AddToCodeBuffer(parameterCount + 1);
  						AddToCodeBuffer(0);
  					}
  					else
  					{
  						short methodRefIndex = itsConstantPool.AddMethodRef(className, methodName, methodType);
  						AddToCodeInt16(methodRefIndex);
  					}
  					break;
  				}
  
  				default:
  				{
  					throw new ArgumentException("bad opcode for method reference");
  				}
  			}
  			itsStackTop = (short)newStack;
  			if (newStack > itsMaxStack)
  			{
  				itsMaxStack = (short)newStack;
  			}
  		}
  
  		/// <summary>Generate code to load the given integer on stack.</summary>
  		/// <remarks>Generate code to load the given integer on stack.</remarks>
  		/// <param name="k">the constant</param>
  		public virtual void AddPush(int k)
  		{
  			if (unchecked((byte)k) == k)
  			{
  				if (k == -1)
  				{
  					Add(ByteCode.ICONST_M1);
  				}
  				else
  				{
  					if (0 <= k && k <= 5)
  					{
  						Add(unchecked((byte)(ByteCode.ICONST_0 + k)));
  					}
  					else
  					{
  						Add(ByteCode.BIPUSH, unchecked((byte)k));
  					}
  				}
  			}
  			else
  			{
  				if ((short)k == k)
  				{
  					Add(ByteCode.SIPUSH, (short)k);
  				}
  				else
  				{
  					AddLoadConstant(k);
  				}
  			}
  		}
  
  		public virtual void AddPush(bool k)
  		{
  			Add(k ? ByteCode.ICONST_1 : ByteCode.ICONST_0);
  		}
  
  		/// <summary>Generate code to load the given long on stack.</summary>
  		/// <remarks>Generate code to load the given long on stack.</remarks>
  		/// <param name="k">the constant</param>
  		public virtual void AddPush(long k)
  		{
  			int ik = (int)k;
  			if (ik == k)
  			{
  				AddPush(ik);
  				Add(ByteCode.I2L);
  			}
  			else
  			{
  				AddLoadConstant(k);
  			}
  		}
  
  		/// <summary>Generate code to load the given double on stack.</summary>
  		/// <remarks>Generate code to load the given double on stack.</remarks>
  		/// <param name="k">the constant</param>
  		public virtual void AddPush(double k)
  		{
  			if (k == 0.0)
  			{
  				// zero
  				Add(ByteCode.DCONST_0);
  				if (1.0 / k < 0)
  				{
  					// Negative zero
  					Add(ByteCode.DNEG);
  				}
  			}
  			else
  			{
  				if (k == 1.0 || k == -1.0)
  				{
  					Add(ByteCode.DCONST_1);
  					if (k < 0)
  					{
  						Add(ByteCode.DNEG);
  					}
  				}
  				else
  				{
  					AddLoadConstant(k);
  				}
  			}
  		}
  
  		/// <summary>
  		/// Generate the code to leave on stack the given string even if the
  		/// string encoding exeeds the class file limit for single string constant
  		/// </summary>
  		/// <param name="k">the constant</param>
  		public virtual void AddPush(string k)
  		{
  			int length = k.Length;
  			int limit = itsConstantPool.GetUtfEncodingLimit(k, 0, length);
  			if (limit == length)
  			{
  				AddLoadConstant(k);
  				return;
  			}
  			// Split string into picies fitting the UTF limit and generate code for
  			// StringBuffer sb = new StringBuffer(length);
  			// sb.append(loadConstant(piece_1));
  			// ...
  			// sb.append(loadConstant(piece_N));
  			// sb.toString();
  			string SB = "java/lang/StringBuffer";
  			Add(ByteCode.NEW, SB);
  			Add(ByteCode.DUP);
  			AddPush(length);
  			AddInvoke(ByteCode.INVOKESPECIAL, SB, "<init>", "(I)V");
  			int cursor = 0;
  			for (; ; )
  			{
  				Add(ByteCode.DUP);
  				string s = Sharpen.Runtime.Substring(k, cursor, limit);
  				AddLoadConstant(s);
  				AddInvoke(ByteCode.INVOKEVIRTUAL, SB, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
  				Add(ByteCode.POP);
  				if (limit == length)
  				{
  					break;
  				}
  				cursor = limit;
  				limit = itsConstantPool.GetUtfEncodingLimit(k, limit, length);
  			}
  			AddInvoke(ByteCode.INVOKEVIRTUAL, SB, "toString", "()Ljava/lang/String;");
  		}
  
  		/// <summary>
  		/// Check if k fits limit on string constant size imposed by class file
  		/// format.
  		/// </summary>
  		/// <remarks>
  		/// Check if k fits limit on string constant size imposed by class file
  		/// format.
  		/// </remarks>
  		/// <param name="k">the string constant</param>
  		public virtual bool IsUnderStringSizeLimit(string k)
  		{
  			return itsConstantPool.IsUnderUtfEncodingLimit(k);
  		}
  
  		/// <summary>Store integer from stack top into the given local.</summary>
  		/// <remarks>Store integer from stack top into the given local.</remarks>
  		/// <param name="local">number of local register</param>
  		public virtual void AddIStore(int local)
  		{
  			Xop(ByteCode.ISTORE_0, ByteCode.ISTORE, local);
  		}
  
  		/// <summary>Store long from stack top into the given local.</summary>
  		/// <remarks>Store long from stack top into the given local.</remarks>
  		/// <param name="local">number of local register</param>
  		public virtual void AddLStore(int local)
  		{
  			Xop(ByteCode.LSTORE_0, ByteCode.LSTORE, local);
  		}
  
  		/// <summary>Store float from stack top into the given local.</summary>
  		/// <remarks>Store float from stack top into the given local.</remarks>
  		/// <param name="local">number of local register</param>
  		public virtual void AddFStore(int local)
  		{
  			Xop(ByteCode.FSTORE_0, ByteCode.FSTORE, local);
  		}
  
  		/// <summary>Store double from stack top into the given local.</summary>
  		/// <remarks>Store double from stack top into the given local.</remarks>
  		/// <param name="local">number of local register</param>
  		public virtual void AddDStore(int local)
  		{
  			Xop(ByteCode.DSTORE_0, ByteCode.DSTORE, local);
  		}
  
  		/// <summary>Store object from stack top into the given local.</summary>
  		/// <remarks>Store object from stack top into the given local.</remarks>
  		/// <param name="local">number of local register</param>
  		public virtual void AddAStore(int local)
  		{
  			Xop(ByteCode.ASTORE_0, ByteCode.ASTORE, local);
  		}
  
  		/// <summary>Load integer from the given local into stack.</summary>
  		/// <remarks>Load integer from the given local into stack.</remarks>
  		/// <param name="local">number of local register</param>
  		public virtual void AddILoad(int local)
  		{
  			Xop(ByteCode.ILOAD_0, ByteCode.ILOAD, local);
  		}
  
  		/// <summary>Load long from the given local into stack.</summary>
  		/// <remarks>Load long from the given local into stack.</remarks>
  		/// <param name="local">number of local register</param>
  		public virtual void AddLLoad(int local)
  		{
  			Xop(ByteCode.LLOAD_0, ByteCode.LLOAD, local);
  		}
  
  		/// <summary>Load float from the given local into stack.</summary>
  		/// <remarks>Load float from the given local into stack.</remarks>
  		/// <param name="local">number of local register</param>
  		public virtual void AddFLoad(int local)
  		{
  			Xop(ByteCode.FLOAD_0, ByteCode.FLOAD, local);
  		}
  
  		/// <summary>Load double from the given local into stack.</summary>
  		/// <remarks>Load double from the given local into stack.</remarks>
  		/// <param name="local">number of local register</param>
  		public virtual void AddDLoad(int local)
  		{
  			Xop(ByteCode.DLOAD_0, ByteCode.DLOAD, local);
  		}
  
  		/// <summary>Load object from the given local into stack.</summary>
  		/// <remarks>Load object from the given local into stack.</remarks>
  		/// <param name="local">number of local register</param>
  		public virtual void AddALoad(int local)
  		{
  			Xop(ByteCode.ALOAD_0, ByteCode.ALOAD, local);
  		}
  
  		/// <summary>Load "this" into stack.</summary>
  		/// <remarks>Load "this" into stack.</remarks>
  		public virtual void AddLoadThis()
  		{
  			Add(ByteCode.ALOAD_0);
  		}
  
  		private void Xop(int shortOp, int op, int local)
  		{
  			switch (local)
  			{
  				case 0:
  				{
  					Add(shortOp);
  					break;
  				}
  
  				case 1:
  				{
  					Add(shortOp + 1);
  					break;
  				}
  
  				case 2:
  				{
  					Add(shortOp + 2);
  					break;
  				}
  
  				case 3:
  				{
  					Add(shortOp + 3);
  					break;
  				}
  
  				default:
  				{
  					Add(op, local);
  					break;
  				}
  			}
  		}
  
  		public virtual int AddTableSwitch(int low, int high)
  		{
  			if (low > high)
  			{
  				throw new ClassFileWriter.ClassFileFormatException("Bad bounds: " + low + ' ' + high);
  			}
  			int newStack = itsStackTop + StackChange(ByteCode.TABLESWITCH);
  			if (newStack < 0 || short.MaxValue < newStack)
  			{
  				BadStack(newStack);
  			}
  			int entryCount = high - low + 1;
  			int padSize = 3 & ~itsCodeBufferTop;
  			// == 3 - itsCodeBufferTop % 4
  			int N = AddReservedCodeSpace(1 + padSize + 4 * (1 + 2 + entryCount));
  			int switchStart = N;
  			itsCodeBuffer[N++] = unchecked((byte)ByteCode.TABLESWITCH);
  			while (padSize != 0)
  			{
  				itsCodeBuffer[N++] = 0;
  				--padSize;
  			}
  			N += 4;
  			// skip default offset
  			N = PutInt32(low, itsCodeBuffer, N);
  			PutInt32(high, itsCodeBuffer, N);
  			itsStackTop = (short)newStack;
  			if (newStack > itsMaxStack)
  			{
  				itsMaxStack = (short)newStack;
  			}
  			return switchStart;
  		}
  
  		public void MarkTableSwitchDefault(int switchStart)
  		{
  			AddSuperBlockStart(itsCodeBufferTop);
  			itsJumpFroms.Put(itsCodeBufferTop, switchStart);
  			SetTableSwitchJump(switchStart, -1, itsCodeBufferTop);
  		}
  
  		public void MarkTableSwitchCase(int switchStart, int caseIndex)
  		{
  			AddSuperBlockStart(itsCodeBufferTop);
  			itsJumpFroms.Put(itsCodeBufferTop, switchStart);
  			SetTableSwitchJump(switchStart, caseIndex, itsCodeBufferTop);
  		}
  
  		public void MarkTableSwitchCase(int switchStart, int caseIndex, int stackTop)
  		{
  			if (!(0 <= stackTop && stackTop <= itsMaxStack))
  			{
  				throw new ArgumentException("Bad stack index: " + stackTop);
  			}
  			itsStackTop = (short)stackTop;
  			AddSuperBlockStart(itsCodeBufferTop);
  			itsJumpFroms.Put(itsCodeBufferTop, switchStart);
  			SetTableSwitchJump(switchStart, caseIndex, itsCodeBufferTop);
  		}
  
  		/// <summary>Set a jump case for a tableswitch instruction.</summary>
  		/// <remarks>
  		/// Set a jump case for a tableswitch instruction. The jump target should
  		/// be marked as a super block start for stack map generation.
  		/// </remarks>
  		public virtual void SetTableSwitchJump(int switchStart, int caseIndex, int jumpTarget)
  		{
  			if (!(0 <= jumpTarget && jumpTarget <= itsCodeBufferTop))
  			{
  				throw new ArgumentException("Bad jump target: " + jumpTarget);
  			}
  			if (!(caseIndex >= -1))
  			{
  				throw new ArgumentException("Bad case index: " + caseIndex);
  			}
  			int padSize = 3 & ~switchStart;
  			// == 3 - switchStart % 4
  			int caseOffset;
  			if (caseIndex < 0)
  			{
  				// default label
  				caseOffset = switchStart + 1 + padSize;
  			}
  			else
  			{
  				caseOffset = switchStart + 1 + padSize + 4 * (3 + caseIndex);
  			}
  			if (!(0 <= switchStart && switchStart <= itsCodeBufferTop - 4 * 4 - padSize - 1))
  			{
  				throw new ArgumentException(switchStart + " is outside a possible range of tableswitch" + " in already generated code");
  			}
  			if ((unchecked((int)(0xFF)) & itsCodeBuffer[switchStart]) != ByteCode.TABLESWITCH)
  			{
  				throw new ArgumentException(switchStart + " is not offset of tableswitch statement");
  			}
  			if (!(0 <= caseOffset && caseOffset + 4 <= itsCodeBufferTop))
  			{
  				// caseIndex >= -1 does not guarantee that caseOffset >= 0 due
  				// to a possible overflow.
  				throw new ClassFileWriter.ClassFileFormatException("Too big case index: " + caseIndex);
  			}
  			// ALERT: perhaps check against case bounds?
  			PutInt32(jumpTarget - switchStart, itsCodeBuffer, caseOffset);
  		}
  
  		public virtual int AcquireLabel()
  		{
  			int top = itsLabelTableTop;
  			if (itsLabelTable == null || top == itsLabelTable.Length)
  			{
  				if (itsLabelTable == null)
  				{
  					itsLabelTable = new int[MIN_LABEL_TABLE_SIZE];
  				}
  				else
  				{
  					int[] tmp = new int[itsLabelTable.Length * 2];
  					System.Array.Copy(itsLabelTable, 0, tmp, 0, top);
  					itsLabelTable = tmp;
  				}
  			}
  			itsLabelTableTop = top + 1;
  			itsLabelTable[top] = -1;
  			return top | unchecked((int)(0x80000000));
  		}
  
  		public virtual void MarkLabel(int label)
  		{
  			if (!(label < 0))
  			{
  				throw new ArgumentException("Bad label, no biscuit");
  			}
  			label &= unchecked((int)(0x7FFFFFFF));
  			if (label > itsLabelTableTop)
  			{
  				throw new ArgumentException("Bad label");
  			}
  			if (itsLabelTable[label] != -1)
  			{
  				throw new InvalidOperationException("Can only mark label once");
  			}
  			itsLabelTable[label] = itsCodeBufferTop;
  		}
  
  		public virtual void MarkLabel(int label, short stackTop)
  		{
  			MarkLabel(label);
  			itsStackTop = stackTop;
  		}
  
  		public virtual void MarkHandler(int theLabel)
  		{
  			itsStackTop = 1;
  			MarkLabel(theLabel);
  		}
  
  		public virtual int GetLabelPC(int label)
  		{
  			if (!(label < 0))
  			{
  				throw new ArgumentException("Bad label, no biscuit");
  			}
  			label &= unchecked((int)(0x7FFFFFFF));
  			if (!(label < itsLabelTableTop))
  			{
  				throw new ArgumentException("Bad label");
  			}
  			return itsLabelTable[label];
  		}
  
  		private void AddLabelFixup(int label, int fixupSite)
  		{
  			if (!(label < 0))
  			{
  				throw new ArgumentException("Bad label, no biscuit");
  			}
  			label &= unchecked((int)(0x7FFFFFFF));
  			if (!(label < itsLabelTableTop))
  			{
  				throw new ArgumentException("Bad label");
  			}
  			int top = itsFixupTableTop;
  			if (itsFixupTable == null || top == itsFixupTable.Length)
  			{
  				if (itsFixupTable == null)
  				{
  					itsFixupTable = new long[MIN_FIXUP_TABLE_SIZE];
  				}
  				else
  				{
  					long[] tmp = new long[itsFixupTable.Length * 2];
  					System.Array.Copy(itsFixupTable, 0, tmp, 0, top);
  					itsFixupTable = tmp;
  				}
  			}
  			itsFixupTableTop = top + 1;
  			itsFixupTable[top] = ((long)label << 32) | fixupSite;
  		}
  
  		private void FixLabelGotos()
  		{
  			byte[] codeBuffer = itsCodeBuffer;
  			for (int i = 0; i < itsFixupTableTop; i++)
  			{
  				long fixup = itsFixupTable[i];
  				int label = (int)(fixup >> 32);
  				int fixupSite = (int)fixup;
  				int pc = itsLabelTable[label];
  				if (pc == -1)
  				{
  					// Unlocated label
  					throw new Exception();
  				}
  				// -1 to get delta from instruction start
  				AddSuperBlockStart(pc);
  				itsJumpFroms.Put(pc, fixupSite - 1);
  				int offset = pc - (fixupSite - 1);
  				if ((short)offset != offset)
  				{
  					throw new ClassFileWriter.ClassFileFormatException("Program too complex: too big jump offset");
  				}
  				codeBuffer[fixupSite] = unchecked((byte)(offset >> 8));
  				codeBuffer[fixupSite + 1] = unchecked((byte)offset);
  			}
  			itsFixupTableTop = 0;
  		}
  
  		/// <summary>Get the current offset into the code of the current method.</summary>
  		/// <remarks>Get the current offset into the code of the current method.</remarks>
  		/// <returns>an integer representing the offset</returns>
  		public virtual int GetCurrentCodeOffset()
  		{
  			return itsCodeBufferTop;
  		}
  
  		public virtual short GetStackTop()
  		{
  			return itsStackTop;
  		}
  
  		public virtual void SetStackTop(short n)
  		{
  			itsStackTop = n;
  		}
  
  		public virtual void AdjustStackTop(int delta)
  		{
  			int newStack = itsStackTop + delta;
  			if (newStack < 0 || short.MaxValue < newStack)
  			{
  				BadStack(newStack);
  			}
  			itsStackTop = (short)newStack;
  			if (newStack > itsMaxStack)
  			{
  				itsMaxStack = (short)newStack;
  			}
  		}
  
  		private void AddToCodeBuffer(int b)
  		{
  			int N = AddReservedCodeSpace(1);
  			itsCodeBuffer[N] = unchecked((byte)b);
  		}
  
  		private void AddToCodeInt16(int value)
  		{
  			int N = AddReservedCodeSpace(2);
  			PutInt16(value, itsCodeBuffer, N);
  		}
  
  		private int AddReservedCodeSpace(int size)
  		{
  			if (itsCurrentMethod == null)
  			{
  				throw new ArgumentException("No method to add to");
  			}
  			int oldTop = itsCodeBufferTop;
  			int newTop = oldTop + size;
  			if (newTop > itsCodeBuffer.Length)
  			{
  				int newSize = itsCodeBuffer.Length * 2;
  				if (newTop > newSize)
  				{
  					newSize = newTop;
  				}
  				byte[] tmp = new byte[newSize];
  				System.Array.Copy(itsCodeBuffer, 0, tmp, 0, oldTop);
  				itsCodeBuffer = tmp;
  			}
  			itsCodeBufferTop = newTop;
  			return oldTop;
  		}
  
  		public virtual void AddExceptionHandler(int startLabel, int endLabel, int handlerLabel, string catchClassName)
  		{
  			if ((startLabel & unchecked((int)(0x80000000))) != unchecked((int)(0x80000000)))
  			{
  				throw new ArgumentException("Bad startLabel");
  			}
  			if ((endLabel & unchecked((int)(0x80000000))) != unchecked((int)(0x80000000)))
  			{
  				throw new ArgumentException("Bad endLabel");
  			}
  			if ((handlerLabel & unchecked((int)(0x80000000))) != unchecked((int)(0x80000000)))
  			{
  				throw new ArgumentException("Bad handlerLabel");
  			}
  			short catch_type_index = (short) ((catchClassName == null) ? 0 : itsConstantPool.AddClass(catchClassName));
  			ExceptionTableEntry newEntry = new ExceptionTableEntry(startLabel, endLabel, handlerLabel, catch_type_index);
  			int N = itsExceptionTableTop;
  			if (N == 0)
  			{
  				itsExceptionTable = new ExceptionTableEntry[ExceptionTableSize];
  			}
  			else
  			{
  				if (N == itsExceptionTable.Length)
  				{
  					ExceptionTableEntry[] tmp = new ExceptionTableEntry[N * 2];
  					System.Array.Copy(itsExceptionTable, 0, tmp, 0, N);
  					itsExceptionTable = tmp;
  				}
  			}
  			itsExceptionTable[N] = newEntry;
  			itsExceptionTableTop = N + 1;
  		}
  
  		public virtual void AddLineNumberEntry(short lineNumber)
  		{
  			if (itsCurrentMethod == null)
  			{
  				throw new ArgumentException("No method to stop");
  			}
  			int N = itsLineNumberTableTop;
  			if (N == 0)
  			{
  				itsLineNumberTable = new int[LineNumberTableSize];
  			}
  			else
  			{
  				if (N == itsLineNumberTable.Length)
  				{
  					int[] tmp = new int[N * 2];
  					System.Array.Copy(itsLineNumberTable, 0, tmp, 0, N);
  					itsLineNumberTable = tmp;
  				}
  			}
  			itsLineNumberTable[N] = (itsCodeBufferTop << 16) + lineNumber;
  			itsLineNumberTableTop = N + 1;
  		}
  
  		/// <summary>
  		/// A stack map table is a code attribute introduced in Java 6 that
  		/// gives type information at key points in the method body (namely, at
  		/// the beginning of each super block after the first).
  		/// </summary>
  		/// <remarks>
  		/// A stack map table is a code attribute introduced in Java 6 that
  		/// gives type information at key points in the method body (namely, at
  		/// the beginning of each super block after the first). Each frame of a
  		/// stack map table contains the state of local variable and operand stack
  		/// for a given super block.
  		/// </remarks>
  		internal sealed class StackMapTable
  		{
  			internal StackMapTable(ClassFileWriter _enclosing)
  			{
  				this._enclosing = _enclosing;
  				this.superBlocks = null;
  				this.locals = this.stack = null;
  				this.workList = null;
  				this.rawStackMap = null;
  				this.localsTop = 0;
  				this.stackTop = 0;
  				this.workListTop = 0;
  				this.rawStackMapTop = 0;
  				this.wide = false;
  			}
  
  			internal void Generate()
  			{
  				this.superBlocks = new SuperBlock[this._enclosing.itsSuperBlockStartsTop];
  				int[] initialLocals = this._enclosing.CreateInitialLocals();
  				for (int i = 0; i < this._enclosing.itsSuperBlockStartsTop; i++)
  				{
  					int start = this._enclosing.itsSuperBlockStarts[i];
  					int end;
  					if (i == this._enclosing.itsSuperBlockStartsTop - 1)
  					{
  						end = this._enclosing.itsCodeBufferTop;
  					}
  					else
  					{
  						end = this._enclosing.itsSuperBlockStarts[i + 1];
  					}
  					this.superBlocks[i] = new SuperBlock(i, start, end, initialLocals);
  				}
  				this.superBlockDeps = this.GetSuperBlockDependencies();
  				this.Verify();
  			}
  
  			private SuperBlock GetSuperBlockFromOffset(int offset)
  			{
  				for (int i = 0; i < this.superBlocks.Length; i++)
  				{
  					SuperBlock sb = this.superBlocks[i];
  					if (sb == null)
  					{
  						break;
  					}
  					else
  					{
  						if (offset >= sb.GetStart() && offset < sb.GetEnd())
  						{
  							return sb;
  						}
  					}
  				}
  				throw new ArgumentException("bad offset: " + offset);
  			}
  
  			/// <summary>
  			/// Determine whether or not an opcode is an actual end to a super
  			/// block.
  			/// </summary>
  			/// <remarks>
  			/// Determine whether or not an opcode is an actual end to a super
  			/// block. This includes any returns or unconditional jumps.
  			/// </remarks>
  			private bool IsSuperBlockEnd(int opcode)
  			{
  				switch (opcode)
  				{
  					case ByteCode.ARETURN:
  					case ByteCode.FRETURN:
  					case ByteCode.IRETURN:
  					case ByteCode.LRETURN:
  					case ByteCode.RETURN:
  					case ByteCode.ATHROW:
  					case ByteCode.GOTO:
  					case ByteCode.GOTO_W:
  					case ByteCode.TABLESWITCH:
  					case ByteCode.LOOKUPSWITCH:
  					{
  						return true;
  					}
  
  					default:
  					{
  						return false;
  					}
  				}
  			}
  
  			/// <summary>Calculate partial dependencies for super blocks.</summary>
  			/// <remarks>
  			/// Calculate partial dependencies for super blocks.
  			/// This is used as a workaround for dead code that is generated. Only
  			/// one dependency per super block is given.
  			/// </remarks>
  			private SuperBlock[] GetSuperBlockDependencies()
  			{
  				SuperBlock[] deps = new SuperBlock[this.superBlocks.Length];
  				for (int i = 0; i < this._enclosing.itsExceptionTableTop; i++)
  				{
  					ExceptionTableEntry ete = this._enclosing.itsExceptionTable[i];
  					short startPC = (short)this._enclosing.GetLabelPC(ete.itsStartLabel);
  					short handlerPC = (short)this._enclosing.GetLabelPC(ete.itsHandlerLabel);
  					SuperBlock handlerSB = this.GetSuperBlockFromOffset(handlerPC);
  					SuperBlock dep = this.GetSuperBlockFromOffset(startPC);
  					deps[handlerSB.GetIndex()] = dep;
  				}
  				int[] targetPCs = this._enclosing.itsJumpFroms.GetKeys();
  				for (int i_1 = 0; i_1 < targetPCs.Length; i_1++)
  				{
  					int targetPC = targetPCs[i_1];
  					int branchPC = this._enclosing.itsJumpFroms.GetInt(targetPC, -1);
  					SuperBlock branchSB = this.GetSuperBlockFromOffset(branchPC);
  					SuperBlock targetSB = this.GetSuperBlockFromOffset(targetPC);
  					deps[targetSB.GetIndex()] = branchSB;
  				}
  				return deps;
  			}
  
  			/// <summary>Get the target super block of a branch instruction.</summary>
  			/// <remarks>Get the target super block of a branch instruction.</remarks>
  			/// <param name="bci">the index of the branch instruction in the code buffer</param>
  			private SuperBlock GetBranchTarget(int bci)
  			{
  				int target;
  				if ((this._enclosing.itsCodeBuffer[bci] & unchecked((int)(0xFF))) == ByteCode.GOTO_W)
  				{
  					target = bci + this.GetOperand(bci + 1, 4);
  				}
  				else
  				{
  					target = bci + (short)this.GetOperand(bci + 1, 2);
  				}
  				return this.GetSuperBlockFromOffset(target);
  			}
  
  			/// <summary>
  			/// Determine whether or not an opcode is a conditional or unconditional
  			/// jump.
  			/// </summary>
  			/// <remarks>
  			/// Determine whether or not an opcode is a conditional or unconditional
  			/// jump.
  			/// </remarks>
  			private bool IsBranch(int opcode)
  			{
  				switch (opcode)
  				{
  					case ByteCode.GOTO:
  					case ByteCode.GOTO_W:
  					case ByteCode.IFEQ:
  					case ByteCode.IFGE:
  					case ByteCode.IFGT:
  					case ByteCode.IFLE:
  					case ByteCode.IFLT:
  					case ByteCode.IFNE:
  					case ByteCode.IFNONNULL:
  					case ByteCode.IFNULL:
  					case ByteCode.IF_ACMPEQ:
  					case ByteCode.IF_ACMPNE:
  					case ByteCode.IF_ICMPEQ:
  					case ByteCode.IF_ICMPGE:
  					case ByteCode.IF_ICMPGT:
  					case ByteCode.IF_ICMPLE:
  					case ByteCode.IF_ICMPLT:
  					case ByteCode.IF_ICMPNE:
  					{
  						return true;
  					}
  
  					default:
  					{
  						return false;
  					}
  				}
  			}
  
  			private int GetOperand(int offset)
  			{
  				return this.GetOperand(offset, 1);
  			}
  
  			/// <summary>Extract a logical operand from the byte code.</summary>
  			/// <remarks>
  			/// Extract a logical operand from the byte code.
  			/// This is used, for example, to get branch offsets.
  			/// </remarks>
  			private int GetOperand(int start, int size)
  			{
  				int result = 0;
  				if (size > 4)
  				{
  					throw new ArgumentException("bad operand size");
  				}
  				for (int i = 0; i < size; i++)
  				{
  					result = (result << 8) | (this._enclosing.itsCodeBuffer[start + i] & unchecked((int)(0xFF)));
  				}
  				return result;
  			}
  
  			/// <summary>
  			/// Calculate initial local variable and op stack types for each super
  			/// block in the method.
  			/// </summary>
  			/// <remarks>
  			/// Calculate initial local variable and op stack types for each super
  			/// block in the method.
  			/// </remarks>
  			private void Verify()
  			{
  				int[] initialLocals = this._enclosing.CreateInitialLocals();
  				this.superBlocks[0].Merge(initialLocals, initialLocals.Length, new int[0], 0, this._enclosing.itsConstantPool);
  				// Start from the top of the method and queue up block dependencies
  				// as they come along.
  				this.workList = new SuperBlock[] { this.superBlocks[0] };
  				this.workListTop = 1;
  				this.ExecuteWorkList();
  				// Replace dead code with no-ops.
  				for (int i = 0; i < this.superBlocks.Length; i++)
  				{
  					SuperBlock sb = this.superBlocks[i];
  					if (!sb.IsInitialized())
  					{
  						this.KillSuperBlock(sb);
  					}
  				}
  				this.ExecuteWorkList();
  			}
  
  			/// <summary>Replace the contents of a super block with no-ops.</summary>
  			/// <remarks>
  			/// Replace the contents of a super block with no-ops.
  			/// The above description is not strictly true; the last instruction is
  			/// an athrow instruction. This technique is borrowed from ASM's
  			/// developer guide: http://asm.ow2.org/doc/developer-guide.html#deadcode
  			/// The proposed algorithm fills a block with nop, ending it with an
  			/// athrow. The stack map generated would be empty locals with an
  			/// exception on the stack. In theory, it shouldn't matter what the
  			/// locals are, as long as the stack has an exception for the athrow bit.
  			/// However, it turns out that if the code being modified falls into an
  			/// exception handler, it causes problems. Therefore, if it does, then
  			/// we steal the locals from the exception block.
  			/// If the block itself is an exception handler, we remove it from the
  			/// exception table to simplify block dependencies.
  			/// </remarks>
  			private void KillSuperBlock(SuperBlock sb)
  			{
  				int[] locals = new int[0];
  				int[] stack = new int[] { TypeInfo.OBJECT("java/lang/Throwable", this._enclosing.itsConstantPool) };
  				// If the super block is handled by any exception handler, use its
  				// locals as the killed block's locals. Ignore uninitialized
  				// handlers, because they will also be killed and removed from the
  				// exception table.
  				for (int i = 0; i < this._enclosing.itsExceptionTableTop; i++)
  				{
  					ExceptionTableEntry ete = this._enclosing.itsExceptionTable[i];
  					int eteStart = this._enclosing.GetLabelPC(ete.itsStartLabel);
  					int eteEnd = this._enclosing.GetLabelPC(ete.itsEndLabel);
  					int handlerPC = this._enclosing.GetLabelPC(ete.itsHandlerLabel);
  					SuperBlock handlerSB = this.GetSuperBlockFromOffset(handlerPC);
  					if ((sb.GetStart() > eteStart && sb.GetStart() < eteEnd) || (eteStart > sb.GetStart() && eteStart < sb.GetEnd()) && handlerSB.IsInitialized())
  					{
  						locals = handlerSB.GetLocals();
  						break;
  					}
  				}
  				// Remove any exception table entry whose handler is the killed
  				// block. This removes block dependencies to make stack maps for
  				// dead blocks easier to create.
  				for (int i_1 = 0; i_1 < this._enclosing.itsExceptionTableTop; i_1++)
  				{
  					ExceptionTableEntry ete = this._enclosing.itsExceptionTable[i_1];
  					int eteStart = this._enclosing.GetLabelPC(ete.itsStartLabel);
  					if (eteStart == sb.GetStart())
  					{
  						for (int j = i_1 + 1; j < this._enclosing.itsExceptionTableTop; j++)
  						{
  							this._enclosing.itsExceptionTable[j - 1] = this._enclosing.itsExceptionTable[j];
  						}
  						this._enclosing.itsExceptionTableTop--;
  						i_1--;
  					}
  				}
  				sb.Merge(locals, locals.Length, stack, stack.Length, this._enclosing.itsConstantPool);
  				int end = sb.GetEnd() - 1;
  				this._enclosing.itsCodeBuffer[end] = unchecked((byte)ByteCode.ATHROW);
  				for (int bci = sb.GetStart(); bci < end; bci++)
  				{
  					this._enclosing.itsCodeBuffer[bci] = unchecked((byte)ByteCode.NOP);
  				}
  			}
  
  			private void ExecuteWorkList()
  			{
  				while (this.workListTop > 0)
  				{
  					SuperBlock work = this.workList[--this.workListTop];
  					work.SetInQueue(false);
  					this.locals = work.GetLocals();
  					this.stack = work.GetStack();
  					this.localsTop = this.locals.Length;
  					this.stackTop = this.stack.Length;
  					this.ExecuteBlock(work);
  				}
  			}
  
  			/// <summary>Simulate the local variable and op stack for a super block.</summary>
  			/// <remarks>Simulate the local variable and op stack for a super block.</remarks>
  			private void ExecuteBlock(SuperBlock work)
  			{
  				int bc = 0;
  				int next = 0;
  				for (int bci = work.GetStart(); bci < work.GetEnd(); bci += next)
  				{
  					bc = this._enclosing.itsCodeBuffer[bci] & unchecked((int)(0xFF));
  					next = this.Execute(bci);
  					// If we have a branch to some super block, we need to merge
  					// the current state of the local table and op stack with what's
  					// currently stored as the initial state of the super block. If
  					// something actually changed, we need to add it to the work
  					// list.
  					if (this.IsBranch(bc))
  					{
  						SuperBlock targetSB = this.GetBranchTarget(bci);
  						this.FlowInto(targetSB);
  					}
  					else
  					{
  						if (bc == ByteCode.TABLESWITCH)
  						{
  							int switchStart = bci + 1 + (3 & ~bci);
  							// 3 - bci % 4
  							int defaultOffset = this.GetOperand(switchStart, 4);
  							SuperBlock targetSB = this.GetSuperBlockFromOffset(bci + defaultOffset);
  							this.FlowInto(targetSB);
  							int low = this.GetOperand(switchStart + 4, 4);
  							int high = this.GetOperand(switchStart + 8, 4);
  
gt: 							int numCases = high - low + 1;