context: /*
    Copyright (C) 2002-2010 Jeroen Frijters
  
    This software is provided 'as-is', without any express or implied
    warranty.  In no event will the authors be held liable for any damages
    arising from the use of this software.
  
    Permission is granted to anyone to use this software for any purpose,
    including commercial applications, and to alter it and redistribute it
    freely, subject to the following restrictions:
  
    1. The origin of this software must not be misrepresented; you must not
       claim that you wrote the original software. If you use this software
       in a product, an acknowledgment in the product documentation would be
       appreciated but is not required.
    2. Altered source versions must be plainly marked as such, and must not be
       misrepresented as being the original software.
    3. This notice may not be removed or altered from any source distribution.
  
    Jeroen Frijters
    jeroen@frijters.net
    
  */
  using System;
  using System.IO;
  using System.Collections.Generic;
  using System.Diagnostics;
  using IKVM.Internal;
  using InstructionFlags = IKVM.Internal.ClassFile.Method.InstructionFlags;
  using ExceptionTableEntry = IKVM.Internal.ClassFile.Method.ExceptionTableEntry;
  
  sealed class InstructionState
  {
  	private struct LocalStoreSites
  	{
  		private int[] data;
  		private int count;
  		private bool shared;
  
  		internal LocalStoreSites Copy()
  		{
  			LocalStoreSites n = new LocalStoreSites();
  			n.data = data;
  			n.count = count;
  			n.shared = true;
  			return n;
  		}
  
  		internal static LocalStoreSites Alloc()
  		{
  			LocalStoreSites n = new LocalStoreSites();
  			n.data = new int[4];
  			return n;
  		}
  
  		internal void Add(int store)
  		{
  			for(int i = 0; i < count; i++)
  			{
  				if(data[i] == store)
  				{
  					return;
  				}
  			}
  			if(count == data.Length)
  			{
  				int[] newarray = new int[data.Length * 2];
  				Buffer.BlockCopy(data, 0, newarray, 0, data.Length * 4);
  				data = newarray;
  				shared = false;
  			}
  			if(shared)
  			{
  				shared = false;
  				data = (int[])data.Clone();
  			}
  			data[count++] = store;
  		}
  
  		internal int this[int index]
  		{
  			get
  			{
  				return data[index];
  			}
  		}
  
  		internal int Count
  		{
  			get
  			{
  				return count;
  			}
  		}
  
  		internal static void MarkShared(LocalStoreSites[] localStoreSites)
  		{
  			for(int i = 0; i < localStoreSites.Length; i++)
  			{
  				localStoreSites[i].shared = true;
  			}
  		}
  	}
  	private TypeWrapper[] stack;
  	private int stackSize;
  	private int stackEnd;
  	private TypeWrapper[] locals;
  	private bool unitializedThis;
  	internal bool changed = true;
  	private enum ShareFlags : byte
  	{
  		None = 0,
  		Stack = 1,
  		Locals = 2,
  		All = Stack | Locals
  	}
  	private ShareFlags flags;
  
  	private InstructionState(TypeWrapper[] stack, int stackSize, int stackEnd, TypeWrapper[] locals, bool unitializedThis)
  	{
  		this.flags = ShareFlags.All;
  		this.stack = stack;
  		this.stackSize = stackSize;
  		this.stackEnd = stackEnd;
  		this.locals = locals;
  		this.unitializedThis = unitializedThis;
  	}
  
  	internal InstructionState(int maxLocals, int maxStack)
  	{
  		this.flags = ShareFlags.None;
  		this.stack = new TypeWrapper[maxStack];
  		this.stackEnd = maxStack;
  		this.locals = new TypeWrapper[maxLocals];
  	}
  
  	internal InstructionState Copy()
  	{
  		return new InstructionState(stack, stackSize, stackEnd, locals, unitializedThis);
  	}
  
  	internal void CopyTo(InstructionState target)
  	{
  		target.flags = ShareFlags.All;
  		target.stack = stack;
  		target.stackSize = stackSize;
  		target.stackEnd = stackEnd;
  		target.locals = locals;
  		target.unitializedThis = unitializedThis;
  		target.changed = true;
  	}
  
  	internal InstructionState CopyLocals()
  	{
  		InstructionState copy = new InstructionState(new TypeWrapper[stack.Length], 0, stack.Length, locals, unitializedThis);
  		copy.flags &= ~ShareFlags.Stack;
  		return copy;
  	}
  
  	public static InstructionState operator+(InstructionState s1, InstructionState s2)
  	{
  		if(s1 == null)
  		{
  			return s2.Copy();
  		}
  		if(s1.stackSize != s2.stackSize || s1.stackEnd != s2.stackEnd)
  		{
  			throw new VerifyError(string.Format("Inconsistent stack height: {0} != {1}",
  				s1.stackSize + s1.stack.Length - s1.stackEnd,
  				s2.stackSize + s2.stack.Length - s2.stackEnd));
  		}
  		InstructionState s = s1.Copy();
  		s.changed = s1.changed;
  		for(int i = 0; i < s.stackSize; i++)
  		{
  			TypeWrapper type = s.stack[i];
  			TypeWrapper type2 = s2.stack[i];
  			if(type == type2)
  			{
  				// perfect match, nothing to do
  			}
  			else if((type == VerifierTypeWrapper.ExtendedDouble && type2 == PrimitiveTypeWrapper.DOUBLE)
  				|| (type2 == VerifierTypeWrapper.ExtendedDouble && type == PrimitiveTypeWrapper.DOUBLE))
  			{
  				if(type != VerifierTypeWrapper.ExtendedDouble)
  				{
  					s.StackCopyOnWrite();
  					s.stack[i] = VerifierTypeWrapper.ExtendedDouble;
  					s.changed = true;
  				}
  			}
  			else if((type == VerifierTypeWrapper.ExtendedFloat && type2 == PrimitiveTypeWrapper.FLOAT)
  				|| (type2 == VerifierTypeWrapper.ExtendedFloat && type == PrimitiveTypeWrapper.FLOAT))
  			{
  				if(type != VerifierTypeWrapper.ExtendedFloat)
  				{
  					s.StackCopyOnWrite();
  					s.stack[i] = VerifierTypeWrapper.ExtendedFloat;
  					s.changed = true;
  				}
  			}
  			else if(!type.IsPrimitive)
  			{
  				TypeWrapper baseType = InstructionState.FindCommonBaseType(type, type2);
  				if(baseType == VerifierTypeWrapper.Invalid)
  				{
  					throw new VerifyError(string.Format("cannot merge {0} and {1}", type.Name, type2.Name));
  				}
  				if(type != baseType)
  				{
  					s.StackCopyOnWrite();
  					s.stack[i] = baseType;
  					s.changed = true;
  				}
  			}
  			else
  			{
  				throw new VerifyError(string.Format("cannot merge {0} and {1}", type.Name, type2.Name));
  			}
  		}
  		for(int i = 0; i < s.locals.Length; i++)
  		{
  			TypeWrapper type = s.locals[i];
  			TypeWrapper type2 = s2.locals[i];
  			TypeWrapper baseType = InstructionState.FindCommonBaseType(type, type2);
  			if(type != baseType)
  			{
  				s.LocalsCopyOnWrite();
  				s.locals[i] = baseType;
  				s.changed = true;
  			}
  		}
  		if(!s.unitializedThis && s2.unitializedThis)
  		{
  			s.unitializedThis = true;
  			s.changed = true;
  		}
  		return s;
  	}
  
  	private static LocalStoreSites MergeStoreSites(LocalStoreSites h1, LocalStoreSites h2)
  	{
  		if(h1.Count == 0)
  		{
  			return h2.Copy();
  		}
  		if(h2.Count == 0)
  		{
  			return h1.Copy();
  		}
  		LocalStoreSites h = h1.Copy();
  		for(int i = 0; i < h2.Count; i++)
  		{
  			h.Add(h2[i]);
  		}
  		return h;
  	}
  
  	internal void SetUnitializedThis(bool state)
  	{
  		unitializedThis = state;
  	}
  
  	internal void CheckUninitializedThis()
  	{
  		if(unitializedThis)
  		{
  			throw new VerifyError("Base class constructor wasn't called");
  		}
  	}
  
  	internal static TypeWrapper FindCommonBaseType(TypeWrapper type1, TypeWrapper type2)
  	{
  		if(type1 == type2)
  		{
  			return type1;
  		}
  		if(type1 == VerifierTypeWrapper.Null)
  		{
  			return type2;
  		}
  		if(type2 == VerifierTypeWrapper.Null)
  		{
  			return type1;
  		}
  		if(type1 == VerifierTypeWrapper.Invalid || type2 == VerifierTypeWrapper.Invalid)
  		{
  			return VerifierTypeWrapper.Invalid;
  		}
  		if(VerifierTypeWrapper.IsFaultBlockException(type1))
  		{
  			VerifierTypeWrapper.ClearFaultBlockException(type1);
  			return FindCommonBaseType(CoreClasses.java.lang.Throwable.Wrapper, type2);
  		}
  		if(VerifierTypeWrapper.IsFaultBlockException(type2))
  		{
  			VerifierTypeWrapper.ClearFaultBlockException(type2);
  			return FindCommonBaseType(type1, CoreClasses.java.lang.Throwable.Wrapper);
  		}
  		if(type1.IsPrimitive || type2.IsPrimitive)
  		{
  			return VerifierTypeWrapper.Invalid;
  		}
  		if(type1 == VerifierTypeWrapper.UninitializedThis || type2 == VerifierTypeWrapper.UninitializedThis)
  		{
  			return VerifierTypeWrapper.Invalid;
  		}
  		if(VerifierTypeWrapper.IsNew(type1) || VerifierTypeWrapper.IsNew(type2))
  		{
  			return VerifierTypeWrapper.Invalid;
  		}
  		if(VerifierTypeWrapper.IsThis(type1))
  		{
  			type1 = ((VerifierTypeWrapper)type1).UnderlyingType;
  		}
  		if(VerifierTypeWrapper.IsThis(type2))
  		{
  			type2 = ((VerifierTypeWrapper)type2).UnderlyingType;
  		}
  		if(type1.IsUnloadable || type2.IsUnloadable)
  		{
  			return VerifierTypeWrapper.Unloadable;
  		}
  		if(type1.ArrayRank > 0 && type2.ArrayRank > 0)
  		{
  			int rank = 1;
  			int rank1 = type1.ArrayRank - 1;
  			int rank2 = type2.ArrayRank - 1;
  			TypeWrapper elem1 = type1.ElementTypeWrapper;
  			TypeWrapper elem2 = type2.ElementTypeWrapper;
  			while(rank1 != 0 && rank2 != 0)
  			{
  				elem1 = elem1.ElementTypeWrapper;
  				elem2 = elem2.ElementTypeWrapper;
  				rank++;
  				rank1--;
  				rank2--;
  			}
  			// NOTE arrays of value types have special merging semantics!
  			// NOTE we don't have to test for the case where the element types are the same, because that
  			// is only relevant if the ranks are the same, but if that is the case the types are completely
  			// identical, in which case the identity test at the top of this method already returned.
  			TypeWrapper baseType;
  			if(elem1.IsPrimitive || elem2.IsPrimitive || elem1.IsNonPrimitiveValueType || elem2.IsNonPrimitiveValueType)
  			{
  				baseType = CoreClasses.java.lang.Object.Wrapper;
  				rank--;
  				if(rank == 0)
  				{
  					return baseType;
  				}
  			}
  			else
  			{
  				baseType = FindCommonBaseTypeHelper(elem1, elem2);
  			}
  			return baseType.MakeArrayType(rank);
  		}
  		return FindCommonBaseTypeHelper(type1, type2);
  	}
  
  	private static TypeWrapper FindCommonBaseTypeHelper(TypeWrapper t1, TypeWrapper t2)
  	{
  		if(t1 == t2)
  		{
  			return t1;
  		}
  		if(t1.IsInterface || t2.IsInterface)
  		{
  			// NOTE according to a paper by Alessandro Coglio & Allen Goldberg titled
  			// "Type Safety in the JVM: Some Problems in Java 2 SDK 1.2 and Proposed Solutions"
  			// the common base of two interfaces is java.lang.Object, and there is special
  			// treatment for java.lang.Object types that allow it to be assigned to any interface
  			// type, the JVM's typesafety then depends on the invokeinterface instruction to make
  			// sure that the reference actually implements the interface.
  			// NOTE the ECMA CLI spec also specifies this interface merging algorithm, so we can't
  			// really do anything more clever than this.
  			return CoreClasses.java.lang.Object.Wrapper;
  		}
  		Stack<TypeWrapper> st1 = new Stack<TypeWrapper>();
  		Stack<TypeWrapper> st2 = new Stack<TypeWrapper>();
  		while(t1 != null)
  		{
  			st1.Push(t1);
  			t1 = t1.BaseTypeWrapper;
  		}
  		while(t2 != null)
  		{
  			st2.Push(t2);
  			t2 = t2.BaseTypeWrapper;
  		}
  		TypeWrapper type = null;
  		for(;;)
  		{
  			t1 = st1.Count > 0 ? st1.Pop() : null;
  			t2 = st2.Count > 0 ? st2.Pop() : null;
  			if(t1 != t2)
  			{
  				return type;
  			}
  			type = t1;
  		}
  	}
  
  	private void SetLocal1(int index, TypeWrapper type)
  	{
  		try
  		{
  			LocalsCopyOnWrite();
  			if(index > 0 && locals[index - 1] != VerifierTypeWrapper.Invalid && locals[index - 1].IsWidePrimitive)
  			{
  				locals[index - 1] = VerifierTypeWrapper.Invalid;
  			}
  			locals[index] = type;
  		}
  		catch(IndexOutOfRangeException)
  		{
  			throw new VerifyError("Illegal local variable number");
  		}
  	}
  
  	private void SetLocal2(int index, TypeWrapper type)
  	{
  		try
  		{
  			LocalsCopyOnWrite();
  			if(index > 0 && locals[index - 1] != VerifierTypeWrapper.Invalid && locals[index - 1].IsWidePrimitive)
  			{
  				locals[index - 1] = VerifierTypeWrapper.Invalid;
  			}
  			locals[index] = type;
  			locals[index + 1] = VerifierTypeWrapper.Invalid;
  		}
  		catch(IndexOutOfRangeException)
  		{
  			throw new VerifyError("Illegal local variable number");
  		}
  	}
  
  	internal void GetLocalInt(int index)
  	{
  		if(GetLocalType(index) != PrimitiveTypeWrapper.INT)
  		{
  			throw new VerifyError("Invalid local type");
  		}
  	}
  
  	internal void SetLocalInt(int index, int instructionIndex)
  	{
  		SetLocal1(index, PrimitiveTypeWrapper.INT);
  	}
  
  	internal void GetLocalLong(int index)
  	{
  		if(GetLocalType(index) != PrimitiveTypeWrapper.LONG)
  		{
  			throw new VerifyError("incorrect local type, not long");
  		}
  	}
  
  	internal void SetLocalLong(int index, int instructionIndex)
  	{
  		SetLocal2(index, PrimitiveTypeWrapper.LONG);
  	}
  
  	internal void GetLocalFloat(int index)
  	{
  		if(GetLocalType(index) != PrimitiveTypeWrapper.FLOAT)
  		{
  			throw new VerifyError("incorrect local type, not float");
  		}
  	}
  
  	internal void SetLocalFloat(int index, int instructionIndex)
  	{
  		SetLocal1(index, PrimitiveTypeWrapper.FLOAT);
  	}
  
  	internal void GetLocalDouble(int index)
  	{
  		if(GetLocalType(index) != PrimitiveTypeWrapper.DOUBLE)
  		{
  			throw new VerifyError("incorrect local type, not double");
  		}
  	}
  
  	internal void SetLocalDouble(int index, int instructionIndex)
  	{
  		SetLocal2(index, PrimitiveTypeWrapper.DOUBLE);
  	}
  
  	internal TypeWrapper GetLocalType(int index)
  	{
  		try
  		{
  			return locals[index];
  		}
  		catch(IndexOutOfRangeException)
  		{
  			throw new VerifyError("Illegal local variable number");
  		}
  	}
  
  	// this is used by the compiler (indirectly, through MethodAnalyzer.GetLocalTypeWrapper),
  	// we've already verified the code so we know we won't run outside the array boundary,
  	// and we don't need to record the fact that we're reading the local.
  	internal TypeWrapper GetLocalTypeEx(int index)
  	{
  		return locals[index];
  	}
  
  	internal void SetLocalType(int index, TypeWrapper type, int instructionIndex)
  	{
  		if(type.IsWidePrimitive)
  		{
  			SetLocal2(index, type);
  		}
  		else
  		{
  			SetLocal1(index, type);
  		}
  	}
  
  	internal void PushType(TypeWrapper type)
  	{
  		if(type.IsIntOnStackPrimitive)
  		{
  			type = PrimitiveTypeWrapper.INT;
  		}
  		PushHelper(type);
  	}
  
  	internal void PushInt()
  	{
  		PushHelper(PrimitiveTypeWrapper.INT);
  	}
  
  	internal void PushLong()
  	{
  		PushHelper(PrimitiveTypeWrapper.LONG);
  	}
  
  	internal void PushFloat()
  	{
  		PushHelper(PrimitiveTypeWrapper.FLOAT);
  	}
  
  	internal void PushExtendedFloat()
  	{
  		PushHelper(VerifierTypeWrapper.ExtendedFloat);
  	}
  
  	internal void PushDouble()
  	{
  		PushHelper(PrimitiveTypeWrapper.DOUBLE);
  	}
  
  	internal void PushExtendedDouble()
  	{
  		PushHelper(VerifierTypeWrapper.ExtendedDouble);
  	}
  
  	internal void PopInt()
  	{
  		if(PopAnyType() != PrimitiveTypeWrapper.INT)
  		{
  			throw new VerifyError("Int expected on stack");
  		}
  	}
  
  	internal bool PopFloat()
  	{
  		TypeWrapper tw = PopAnyType();
  		if(tw != PrimitiveTypeWrapper.FLOAT && tw != VerifierTypeWrapper.ExtendedFloat)
  		{
  			throw new VerifyError("Float expected on stack");
  		}
  		return tw == VerifierTypeWrapper.ExtendedFloat;
  	}
  
  	internal bool PopDouble()
  	{
  		TypeWrapper tw = PopAnyType();
  		if(tw != PrimitiveTypeWrapper.DOUBLE && tw != VerifierTypeWrapper.ExtendedDouble)
  		{
  			throw new VerifyError("Double expected on stack");
  		}
  		return tw == VerifierTypeWrapper.ExtendedDouble;
  	}
  
  	internal void PopLong()
  	{
  		if(PopAnyType() != PrimitiveTypeWrapper.LONG)
  		{
  			throw new VerifyError("Long expected on stack");
  		}
  	}
  
  	internal TypeWrapper PopArrayType()
  	{
  		TypeWrapper type = PopAnyType();
  		if(!VerifierTypeWrapper.IsNullOrUnloadable(type) && type.ArrayRank == 0)
  		{
  			throw new VerifyError("Array reference expected on stack");
  		}
  		return type;
  	}
  
  	// null or an initialized object reference
  	internal TypeWrapper PopObjectType()
  	{
  		TypeWrapper type = PopType();
  		if(type.IsPrimitive || VerifierTypeWrapper.IsNew(type) || type == VerifierTypeWrapper.UninitializedThis)
  		{
  			throw new VerifyError("Expected object reference on stack");
  		}
  		return type;
  	}
  
  	// null or an initialized object reference derived from baseType (or baseType)
  	internal TypeWrapper PopObjectType(TypeWrapper baseType)
  	{
  		TypeWrapper type = PopObjectType();
  		// HACK because of the way interfaces references works, if baseType
  		// is an interface or array of interfaces, any reference will be accepted
  		if(!baseType.IsUnloadable && !baseType.IsInterfaceOrInterfaceArray && !(type.IsUnloadable || type.IsAssignableTo(baseType)))
  		{
  			throw new VerifyError("Unexpected type " + type.Name + " where " + baseType.Name + " was expected");
  		}
  		return type;
  	}
  
  	internal TypeWrapper PeekType()
  	{
  		if(stackSize == 0)
  		{
  			throw new VerifyError("Unable to pop operand off an empty stack");
  		}
  		return stack[stackSize - 1];
  	}
  
  	internal void MultiPopAnyType(int count)
  	{
  		while(count-- != 0)
  		{
  			PopAnyType();
  		}
  	}
  
  	internal TypeWrapper PopFaultBlockException()
  	{
  		return stack[--stackSize];
  	}
  
  	internal TypeWrapper PopAnyType()
  	{
  		if(stackSize == 0)
  		{
  			throw new VerifyError("Unable to pop operand off an empty stack");
  		}
  		TypeWrapper type = stack[--stackSize];
  		if(type.IsWidePrimitive || type == VerifierTypeWrapper.ExtendedDouble)
  		{
  			stackEnd++;
  		}
  		if(VerifierTypeWrapper.IsThis(type))
  		{
  			type = ((VerifierTypeWrapper)type).UnderlyingType;
  		}
  		if(VerifierTypeWrapper.IsFaultBlockException(type))
  		{
  			VerifierTypeWrapper.ClearFaultBlockException(type);
  			type = CoreClasses.java.lang.Throwable.Wrapper;
  		}
  		return type;
  	}
  
  	// NOTE this can *not* be used to pop double or long
  	internal TypeWrapper PopType()
  	{
  		TypeWrapper type = PopAnyType();
  		if(type.IsWidePrimitive || type == VerifierTypeWrapper.ExtendedDouble)
  		{
  			throw new VerifyError("Attempt to split long or double on the stack");
  		}
  		return type;
  	}
  
  	// this will accept null, a primitive type of the specified type or an initialized reference of the
  	// specified type or derived from it
  	// NOTE this can also be used to pop double or long
  	internal TypeWrapper PopType(TypeWrapper baseType)
  	{
  		if(baseType.IsIntOnStackPrimitive)
  		{
  			baseType = PrimitiveTypeWrapper.INT;
  		}
  		TypeWrapper type = PopAnyType();
  		if(VerifierTypeWrapper.IsNew(type) || type == VerifierTypeWrapper.UninitializedThis)
  		{
  			throw new VerifyError("Expecting to find object/array on stack");
  		}
  		if(type != baseType &&
  			!((type.IsUnloadable && !baseType.IsPrimitive) || (baseType.IsUnloadable && !type.IsPrimitive) ||
  				type.IsAssignableTo(baseType)))
  		{
  			// HACK because of the way interfaces references works, if baseType
  			// is an interface or array of interfaces, any reference will be accepted
  			if(baseType.IsInterfaceOrInterfaceArray && !type.IsPrimitive)
  			{
  				return type;
  			}
  			if(type == VerifierTypeWrapper.ExtendedDouble && baseType == PrimitiveTypeWrapper.DOUBLE)
  			{
  				return type;
  			}
  			if(type == VerifierTypeWrapper.ExtendedFloat && baseType == PrimitiveTypeWrapper.FLOAT)
  			{
  				return type;
  			}
  			throw new VerifyError("Unexpected type " + type.Name + " where " + baseType.Name + " was expected");
  		}
  		return type;
  	}
  
  	internal int GetStackHeight()
  	{
  		return stackSize;
  	}
  
  	internal TypeWrapper GetStackSlot(int pos)
  	{
  		TypeWrapper tw = stack[stackSize - 1 - pos];
  		if(tw == VerifierTypeWrapper.ExtendedDouble)
  		{
  			tw = PrimitiveTypeWrapper.DOUBLE;
  		}
  		else if(tw == VerifierTypeWrapper.ExtendedFloat)
  		{
  			tw = PrimitiveTypeWrapper.FLOAT;
  		}
  		return tw;
  	}
  
  	internal TypeWrapper GetStackSlotEx(int pos)
  	{
  		return stack[stackSize - 1 - pos];
  	}
  
  	internal TypeWrapper GetStackByIndex(int index)
  	{
  		return stack[index];
  	}
  
  	private void PushHelper(TypeWrapper type)
  	{
  		if(type.IsWidePrimitive || type == VerifierTypeWrapper.ExtendedDouble)
  		{
  			stackEnd--;
  		}
  		if(stackSize >= stackEnd)
  		{
  			throw new VerifyError("Stack overflow");
  		}
  		StackCopyOnWrite();
  		stack[stackSize++] = type;
  	}
  
  	internal void MarkInitialized(TypeWrapper type, TypeWrapper initType, int instructionIndex)
  	{
  		System.Diagnostics.Debug.Assert(type != null && initType != null);
  
  		for(int i = 0; i < locals.Length; i++)
  		{
  			if(locals[i] == type)
  			{
  				LocalsCopyOnWrite();
  				locals[i] = initType;
  			}
  		}
  		for(int i = 0; i < stackSize; i++)
  		{
  			if(stack[i] == type)
  			{
  				StackCopyOnWrite();
  				stack[i] = initType;
  			}
  		}
  	}
  
  	private void StackCopyOnWrite()
  	{
  		if((flags & ShareFlags.Stack) != 0)
  		{
  			flags &= ~ShareFlags.Stack;
  			stack = (TypeWrapper[])stack.Clone();
  		}
  	}
  
  	private void LocalsCopyOnWrite()
  	{
  		if((flags & ShareFlags.Locals) != 0)
  		{
  			flags &= ~ShareFlags.Locals;
  			locals = (TypeWrapper[])locals.Clone();
  		}
  	}
  
  	internal void DumpLocals()
  	{
  		Console.Write("// ");
  		string sep = "";
  		for(int i = 0; i < locals.Length; i++)
  		{
  			Console.Write(sep);
  			Console.Write(locals[i]);
  			sep = ", ";
  		}
  		Console.WriteLine();
  	}
  
  	internal void DumpStack()
  	{
  		Console.Write("// ");
  		string sep = "";
  		for(int i = 0; i < stackSize; i++)
  		{
  			Console.Write(sep);
  			Console.Write(stack[i]);
  			sep = ", ";
  		}
  		Console.WriteLine();
  	}
  
  	// this method ensures that no uninitialized object are in the current state
  	internal void CheckUninitializedObjRefs()
  	{
  		for(int i = 0; i < locals.Length; i++)
  		{
  			TypeWrapper type = locals[i];
  			if(VerifierTypeWrapper.IsNew(type))
  			{
  				throw new VerifyError("uninitialized object ref in local (2)");
  			}
  		}
  		for(int i = 0; i < stackSize; i++)
  		{
  			TypeWrapper type = stack[i];
  			if(VerifierTypeWrapper.IsNew(type))
  			{
  				throw new VerifyError("uninitialized object ref on stack");
  			}
  		}
  	}
  
  	internal void ClearFaultBlockException()
  	{
  		if(VerifierTypeWrapper.IsFaultBlockException(stack[0]))
  		{
  			StackCopyOnWrite();
  			changed = true;
  			stack[0] = CoreClasses.java.lang.Throwable.Wrapper;
  		}
  	}
  }
  
  struct StackState
  {
  	private InstructionState state;
  	private int sp;
  
  	internal StackState(InstructionState state)
  	{
  		this.state = state;
  		sp = state.GetStackHeight();
  	}
  
  	internal TypeWrapper PeekType()
  	{
  		if(sp == 0)
  		{
  			throw new VerifyError("Unable to pop operand off an empty stack");
  		}
  		TypeWrapper type = state.GetStackByIndex(sp - 1);
  		if(VerifierTypeWrapper.IsThis(type))
  		{
  			type = ((VerifierTypeWrapper)type).UnderlyingType;
  		}
  		return type;
  	}
  
  	internal TypeWrapper PopAnyType()
  	{
  		if(sp == 0)
  		{
  			throw new VerifyError("Unable to pop operand off an empty stack");
  		}
  		TypeWrapper type = state.GetStackByIndex(--sp);
  		if(VerifierTypeWrapper.IsThis(type))
  		{
  			type = ((VerifierTypeWrapper)type).UnderlyingType;
  		}
  		if(VerifierTypeWrapper.IsFaultBlockException(type))
  		{
  			VerifierTypeWrapper.ClearFaultBlockException(type);
  			type = CoreClasses.java.lang.Throwable.Wrapper;
  		}
  		return type;
  	}
  
  	internal TypeWrapper PopType(TypeWrapper baseType)
  	{
  		if(baseType.IsIntOnStackPrimitive)
  		{
  			baseType = PrimitiveTypeWrapper.INT;
  		}
  		TypeWrapper type = PopAnyType();
  		if(VerifierTypeWrapper.IsNew(type) || type == VerifierTypeWrapper.UninitializedThis)
  		{
  			throw new VerifyError("Expecting to find object/array on stack");
  		}
  		if(type != baseType &&
  			!((type.IsUnloadable && !baseType.IsPrimitive) || (baseType.IsUnloadable && !type.IsPrimitive) ||
  			type.IsAssignableTo(baseType)))
  		{
  			// HACK because of the way interfaces references works, if baseType
  			// is an interface or array of interfaces, any reference will be accepted
  			if(baseType.IsInterfaceOrInterfaceArray && !type.IsPrimitive)
  			{
  				return type;
  			}
  			if(type == VerifierTypeWrapper.ExtendedDouble && baseType == PrimitiveTypeWrapper.DOUBLE)
  			{
  				return type;
  			}
  			if(type == VerifierTypeWrapper.ExtendedFloat && baseType == PrimitiveTypeWrapper.FLOAT)
  			{
  				return type;
  			}
  			throw new VerifyError("Unexpected type " + type.Name + " where " + baseType.Name + " was expected");
  		}
  		return type;
  	}
  
  	// NOTE this can *not* be used to pop double or long
  	internal TypeWrapper PopType()
  	{
  		TypeWrapper type = PopAnyType();
  		if(type.IsWidePrimitive || type == VerifierTypeWrapper.ExtendedDouble)
  		{
  			throw new VerifyError("Attempt to split long or double on the stack");
  		}
  		return type;
  	}
  
  	internal void PopInt()
  	{
  		if(PopAnyType() != PrimitiveTypeWrapper.INT)
  		{
  			throw new VerifyError("Int expected on stack");
  		}
  	}
  
  	internal void PopFloat()
  	{
  		TypeWrapper tw = PopAnyType();
  		if(tw != PrimitiveTypeWrapper.FLOAT && tw != VerifierTypeWrapper.ExtendedFloat)
  		{
  			throw new VerifyError("Float expected on stack");
  		}
  	}
  
  	internal void PopDouble()
  	{
  		TypeWrapper tw = PopAnyType();
  		if(tw != PrimitiveTypeWrapper.DOUBLE && tw != VerifierTypeWrapper.ExtendedDouble)
  		{
  			throw new VerifyError("Double expected on stack");
  		}
  	}
  
  	internal void PopLong()
  	{
  		if(PopAnyType() != PrimitiveTypeWrapper.LONG)
  		{
  			throw new VerifyError("Long expected on stack");
  		}
  	}
  
  	internal TypeWrapper PopArrayType()
  	{
  		TypeWrapper type = PopAnyType();
  		if(!VerifierTypeWrapper.IsNullOrUnloadable(type) && type.ArrayRank == 0)
  		{
  			throw new VerifyError("Array reference expected on stack");
  		}
  		return type;
  	}
  
  	// either null or an initialized object reference
  	internal TypeWrapper PopObjectType()
  	{
  		TypeWrapper type = PopAnyType();
  		if(type.IsPrimitive || VerifierTypeWrapper.IsNew(type) || type == VerifierTypeWrapper.UninitializedThis)
  		{
  			throw new VerifyError("Expected object reference on stack");
  		}
  		return type;
  	}
  
  	// null or an initialized object reference derived from baseType (or baseType)
  	internal TypeWrapper PopObjectType(TypeWrapper baseType)
  	{
  		TypeWrapper type = PopObjectType();
  		// HACK because of the way interfaces references works, if baseType
  		// is an interface or array of interfaces, any reference will be accepted
  		if(!baseType.IsUnloadable && !baseType.IsInterfaceOrInterfaceArray && !(type.IsUnloadable || type.IsAssignableTo(baseType)))
  		{
  			throw new VerifyError("Unexpected type " + type + " where " + baseType + " was expected");
  		}
  		return type;
  	}
  }
  
  sealed class ExceptionSorter : IComparer<ExceptionTableEntry>
  {
  	public int Compare(ExceptionTableEntry e1, ExceptionTableEntry e2)
  	{
  		if (e1.startIndex < e2.startIndex)
  		{
  			return -1;
  		}
  		if (e1.startIndex == e2.startIndex)
  		{
  			if (e1.endIndex == e2.endIndex)
  			{
  				if (e1.ordinal > e2.ordinal)
  				{
  					return -1;
  				}
  				if (e1.ordinal == e2.ordinal)
  				{
  					return 0;
  				}
  				return 1;
  			}
  			if (e1.endIndex > e2.endIndex)
  			{
  				return -1;
  			}
  		}
  		return 1;
  	}
  }
  
  struct UntangledExceptionTable
  {
  	private readonly ExceptionTableEntry[] exceptions;
  
  	internal UntangledExceptionTable(ExceptionTableEntry[] exceptions)
  	{
  #if DEBUG
  		for (int i = 0; i < exceptions.Length; i++)
  		{
  			for (int j = i + 1; j < exceptions.Length; j++)
  			{
  				// check for partially overlapping try blocks (which is legal for the JVM, but not the CLR)
  				if (exceptions[i].startIndex < exceptions[j].startIndex &&
  					exceptions[j].startIndex < exceptions[i].endIndex &&
  					exceptions[i].endIndex < exceptions[j].endIndex)
  				{
  					throw new InvalidOperationException("Partially overlapping try blocks is broken");
  				}
  				// check that we didn't destroy the ordering, when sorting
  				if (exceptions[i].startIndex <= exceptions[j].startIndex &&
  					exceptions[i].endIndex >= exceptions[j].endIndex &&
  					exceptions[i].ordinal < exceptions[j].ordinal)
  				{
  					throw new InvalidOperationException("Non recursive try blocks is broken");
  				}
  			}
  		}
  #endif
  		this.exceptions = exceptions;
  	}
  
  	internal ExceptionTableEntry this[int index]
  	{
  		get { return exceptions[index]; }
  	}
  
  	internal int Length
  	{
  		get { return exceptions.Length; }
  	}
  
  	internal void SetFinally(int index)
  	{
  		exceptions[index] = new ExceptionTableEntry(exceptions[index].startIndex, exceptions[index].endIndex, exceptions[index].handlerIndex, exceptions[index].catch_type, exceptions[index].ordinal, true);
  	}
  }
  
  struct CodeInfo
  {
  	private readonly InstructionState[] state;
  
  	internal CodeInfo(InstructionState[] state)
  	{
  		this.state = state;
  	}
  
  	internal bool HasState(int index)
  	{
  		return state[index] != null;
  	}
  
  	internal int GetStackHeight(int index)
  	{
  		return state[index].GetStackHeight();
  	}
  
  	internal TypeWrapper GetStackTypeWrapper(int index, int pos)
  	{
  		TypeWrapper type = state[index].GetStackSlot(pos);
  		if (VerifierTypeWrapper.IsThis(type))
  		{
  			type = ((VerifierTypeWrapper)type).UnderlyingType;
  		}
  		return type;
  	}
  
  	internal TypeWrapper GetRawStackTypeWrapper(int index, int pos)
  	{
  		return state[index].GetStackSlot(pos);
  	}
  
  	internal bool IsStackTypeExtendedDouble(int index, int pos)
  	{
  		return state[index].GetStackSlotEx(pos) == VerifierTypeWrapper.ExtendedDouble;
  	}
  
  	internal TypeWrapper GetLocalTypeWrapper(int index, int local)
  	{
  		return state[index].GetLocalTypeEx(local);
  	}
  }
  
  sealed class MethodAnalyzer
  {
  	private readonly static TypeWrapper ByteArrayType;
  	private readonly static TypeWrapper BooleanArrayType;
  	private readonly static TypeWrapper ShortArrayType;
  	private readonly static TypeWrapper CharArrayType;
  	private readonly static TypeWrapper IntArrayType;
  	private readonly static TypeWrapper FloatArrayType;
  	private readonly static TypeWrapper DoubleArrayType;
  	private readonly static TypeWrapper LongArrayType;
  	private readonly static TypeWrapper java_lang_ThreadDeath;
  	private readonly TypeWrapper wrapper;
  	private readonly MethodWrapper mw;
  	private readonly ClassFile classFile;
  	private readonly ClassFile.Method method;
  	private readonly ClassLoaderWrapper classLoader;
  	private readonly TypeWrapper thisType;
  	private readonly InstructionState[] state;
  	private List<string> errorMessages;
  	private readonly Dictionary<int, TypeWrapper> newTypes = new Dictionary<int, TypeWrapper>();
  	private readonly Dictionary<int, TypeWrapper> faultTypes = new Dictionary<int, TypeWrapper>();
  
  	static MethodAnalyzer()
  	{
  		ByteArrayType = PrimitiveTypeWrapper.BYTE.MakeArrayType(1);
  		BooleanArrayType = PrimitiveTypeWrapper.BOOLEAN.MakeArrayType(1);
  		ShortArrayType = PrimitiveTypeWrapper.SHORT.MakeArrayType(1);
  		CharArrayType = PrimitiveTypeWrapper.CHAR.MakeArrayType(1);
  		IntArrayType = PrimitiveTypeWrapper.INT.MakeArrayType(1);
  		FloatArrayType = PrimitiveTypeWrapper.FLOAT.MakeArrayType(1);
  		DoubleArrayType = PrimitiveTypeWrapper.DOUBLE.MakeArrayType(1);
  		LongArrayType = PrimitiveTypeWrapper.LONG.MakeArrayType(1);
  		java_lang_ThreadDeath = ClassLoaderWrapper.LoadClassCritical("java.lang.ThreadDeath");
  	}
  
  	internal MethodAnalyzer(TypeWrapper wrapper, MethodWrapper mw, ClassFile classFile, ClassFile.Method method, ClassLoaderWrapper classLoader)
  	{
  		if(method.VerifyError != null)
  		{
  			throw new VerifyError(method.VerifyError);
  		}
  
  		this.wrapper = wrapper;
  		this.mw = mw;
  		this.classFile = classFile;
  		this.method = method;
  		this.classLoader = classLoader;
  		state = new InstructionState[method.Instructions.Length];
  
  		try
  		{
  			// ensure that exception blocks and handlers start and end at instruction boundaries
  			for(int i = 0; i < method.ExceptionTable.Length; i++)
  			{
  				int start = method.ExceptionTable[i].startIndex;
  				int end = method.ExceptionTable[i].endIndex;
  				int handler = method.ExceptionTable[i].handlerIndex;
  				if(start >= end || start == -1 || end == -1 || handler <= 0)
  				{
  					throw new IndexOutOfRangeException();
  				}
  			}
  		}
  		catch(IndexOutOfRangeException)
  		{
  			// TODO figure out if we should throw this during class loading
  			throw new ClassFormatError(string.Format("Illegal exception table (class: {0}, method: {1}, signature: {2}", classFile.Name, method.Name, method.Signature));
  		}
  
  		// start by computing the initial state, the stack is empty and the locals contain the arguments
  		state[0] = new InstructionState(method.MaxLocals, method.MaxStack);
  		int firstNonArgLocalIndex = 0;
  		if(!method.IsStatic)
  		{
  			thisType = VerifierTypeWrapper.MakeThis(wrapper);
  			// this reference. If we're a constructor, the this reference is uninitialized.
  			if(ReferenceEquals(method.Name, StringConstants.INIT))
  			{
  				state[0].SetLocalType(firstNonArgLocalIndex++, VerifierTypeWrapper.UninitializedThis, -1);
  				state[0].SetUnitializedThis(true);
  			}
  			else
  			{
  				state[0].SetLocalType(firstNonArgLocalIndex++, thisType, -1);
  			}
  		}
  		else
  		{
  			thisType = null;
  		}
  		// mw can be null when we're invoked from IsSideEffectFreeStaticInitializer
  		TypeWrapper[] argTypeWrappers = mw == null ? TypeWrapper.EmptyArray : mw.GetParameters();
  		for(int i = 0; i < argTypeWrappers.Length; i++)
  		{
  			TypeWrapper type = argTypeWrappers[i];
  			if(type.IsIntOnStackPrimitive)
  			{
  				type = PrimitiveTypeWrapper.INT;
  			}
  			state[0].SetLocalType(firstNonArgLocalIndex++, type, -1);
  			if(type.IsWidePrimitive)
  			{
  				firstNonArgLocalIndex++;
  			}
  		}
  		AnalyzeTypeFlow();
  		VerifyPassTwo();
  	}
  
  	internal CodeInfo GetCodeInfoAndErrors(UntangledExceptionTable exceptions, out List<string> errors)
  	{
  		CodeInfo codeInfo = new CodeInfo(state);
  		OptimizationPass(codeInfo, classFile, method, exceptions, wrapper, classLoader);
  		PatchHardErrorsAndDynamicMemberAccess(wrapper, mw);
  		errors = errorMessages;
  		if (AnalyzePotentialFaultBlocks(codeInfo, method, exceptions))
  		{
  			AnalyzeTypeFlow();
  		}
  		ConvertFinallyBlocks(codeInfo, method, exceptions);
  		return codeInfo;
  	}
  
  	private void AnalyzeTypeFlow()
  	{
  		InstructionState s = new InstructionState(method.MaxLocals, method.MaxStack);
  		bool done = false;
  		ClassFile.Method.Instruction[] instructions = method.Instructions;
  		while(!done)
  		{
  			done = true;
  			for(int i = 0; i < instructions.Length; i++)
  			{
  				if(state[i] != null && state[i].changed)
  				{
  					try
  					{
  						//Console.WriteLine(method.Instructions[i].PC + ": " + method.Instructions[i].OpCode.ToString());
  						done = false;
  						state[i].changed = false;
  						// mark the exception handlers reachable from this instruction
  						for(int j = 0; j < method.ExceptionTable.Length; j++)
  						{
  							if(method.ExceptionTable[j].startIndex <= i && i < method.ExceptionTable[j].endIndex)
  							{
  								int idx = method.ExceptionTable[j].handlerIndex;
  								InstructionState ex = state[i].CopyLocals();
  								int catch_type = method.ExceptionTable[j].catch_type;
  								if(catch_type == 0)
  								{
  									TypeWrapper tw;
  									if (!faultTypes.TryGetValue(idx, out tw))
  									{
  										tw = VerifierTypeWrapper.MakeFaultBlockException(this, idx);
  										faultTypes.Add(idx, tw);
  									}
  									ex.PushType(tw);
  								}
  								else
  								{
  									// TODO if the exception type is unloadable we should consider pushing
  									// Throwable as the type and recording a loader constraint
  									ex.PushType(GetConstantPoolClassType(catch_type));
  								}
  								state[idx] += ex;
  							}
  						}
  						state[i].CopyTo(s);
  						ClassFile.Method.Instruction instr = instructions[i];
  						switch(instr.NormalizedOpCode)
  						{
  							case NormalizedByteCode.__aload:
  							{
  								TypeWrapper type = s.GetLocalType(instr.NormalizedArg1);
  								if(type == VerifierTypeWrapper.Invalid || type.IsPrimitive)
  								{
  									throw new VerifyError("Object reference expected");
  								}
  								s.PushType(type);
  								break;
  							}
  							case NormalizedByteCode.__astore:
  							{
  								if(VerifierTypeWrapper.IsFaultBlockException(s.PeekType()))
  								{
  									s.SetLocalType(instr.NormalizedArg1, s.PopFaultBlockException(), i);
  									break;
  								}
  								// NOTE since the reference can be uninitialized, we cannot use PopObjectType
  								TypeWrapper type = s.PopType();
  								if(type.IsPrimitive)
  								{
  									throw new VerifyError("Object reference expected");
  								}
  								s.SetLocalType(instr.NormalizedArg1, type, i);
  								break;
  							}
  							case NormalizedByteCode.__aconst_null:
  								s.PushType(VerifierTypeWrapper.Null);
  								break;
  							case NormalizedByteCode.__aaload:
  							{
  								s.PopInt();
  								TypeWrapper type = s.PopArrayType();
  								if(type == VerifierTypeWrapper.Null)
  								{
  									// if the array is null, we have use null as the element type, because
  									// otherwise the rest of the code will not verify correctly
  									s.PushType(VerifierTypeWrapper.Null);
  								}
  								else if(type.IsUnloadable)
  								{
  									s.PushType(VerifierTypeWrapper.Unloadable);
  								}
  								else
  								{
  									type = type.ElementTypeWrapper;
  									if(type.IsPrimitive)
  									{
  										throw new VerifyError("Object array expected");
  									}
  									s.PushType(type);
  								}
  								break;
  							}
  							case NormalizedByteCode.__aastore:
  								s.PopObjectType();
  								s.PopInt();
  								s.PopArrayType();
  								// TODO check that elem is assignable to the array
  								break;
  							case NormalizedByteCode.__baload:
  							{
  								s.PopInt();
  								TypeWrapper type = s.PopArrayType();
  								if(!VerifierTypeWrapper.IsNullOrUnloadable(type) &&
  									type != ByteArrayType &&
  									type != BooleanArrayType)
  								{
  									throw new VerifyError();
  								}
  								s.PushInt();
  								break;
  							}
  							case NormalizedByteCode.__bastore:
  							{
  								s.PopInt();
  								s.PopInt();
  								TypeWrapper type = s.PopArrayType();
  								if(!VerifierTypeWrapper.IsNullOrUnloadable(type) &&
  									type != ByteArrayType &&
  									type != BooleanArrayType)
  								{
  									throw new VerifyError();
  								}
  								break;
  							}
  							case NormalizedByteCode.__caload:
  								s.PopInt();
  								s.PopObjectType(CharArrayType);
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__castore:
  								s.PopInt();
  								s.PopInt();
  								s.PopObjectType(CharArrayType);
  								break;
  							case NormalizedByteCode.__saload:
  								s.PopInt();
  								s.PopObjectType(ShortArrayType);
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__sastore:
  								s.PopInt();
  								s.PopInt();
  								s.PopObjectType(ShortArrayType);
  								break;
  							case NormalizedByteCode.__iaload:
  								s.PopInt();
  								s.PopObjectType(IntArrayType);
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__iastore:
  								s.PopInt();
  								s.PopInt();
  								s.PopObjectType(IntArrayType);
  								break;
  							case NormalizedByteCode.__laload:
  								s.PopInt();
  								s.PopObjectType(LongArrayType);
  								s.PushLong();
  								break;
  							case NormalizedByteCode.__lastore:
  								s.PopLong();
  								s.PopInt();
  								s.PopObjectType(LongArrayType);
  								break;
  							case NormalizedByteCode.__daload:
  								s.PopInt();
  								s.PopObjectType(DoubleArrayType);
  								s.PushDouble();
  								break;
  							case NormalizedByteCode.__dastore:
  								s.PopDouble();
  								s.PopInt();
  								s.PopObjectType(DoubleArrayType);
  								break;
  							case NormalizedByteCode.__faload:
  								s.PopInt();
  								s.PopObjectType(FloatArrayType);
  								s.PushFloat();
  								break;
  							case NormalizedByteCode.__fastore:
  								s.PopFloat();
  								s.PopInt();
  								s.PopObjectType(FloatArrayType);
  								break;
  							case NormalizedByteCode.__arraylength:
  								s.PopArrayType();
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__iconst:
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__if_icmpeq:
  							case NormalizedByteCode.__if_icmpne:
  							case NormalizedByteCode.__if_icmplt:
  							case NormalizedByteCode.__if_icmpge:
  							case NormalizedByteCode.__if_icmpgt:
  							case NormalizedByteCode.__if_icmple:
  								s.PopInt();
  								s.PopInt();
  								break;
  							case NormalizedByteCode.__ifeq:
  							case NormalizedByteCode.__ifge:
  							case NormalizedByteCode.__ifgt:
  							case NormalizedByteCode.__ifle:
  							case NormalizedByteCode.__iflt:
  							case NormalizedByteCode.__ifne:
  								s.PopInt();
  								break;
  							case NormalizedByteCode.__ifnonnull:
  							case NormalizedByteCode.__ifnull:
  								// TODO it might be legal to use an unitialized ref here
  								s.PopObjectType();
  								break;
  							case NormalizedByteCode.__if_acmpeq:
  							case NormalizedByteCode.__if_acmpne:
  								// TODO it might be legal to use an unitialized ref here
  								s.PopObjectType();
  								s.PopObjectType();
  								break;
  							case NormalizedByteCode.__getstatic:
  							case NormalizedByteCode.__dynamic_getstatic:
  								// special support for when we're being called from IsSideEffectFreeStaticInitializer
  								if(mw == null)
  								{
  									switch(GetFieldref(instr.Arg1).Signature[0])
  									{
  										case 'B':
  										case 'Z':
  										case 'C':
  										case 'S':
  										case 'I':
  											s.PushInt();
  											break;
  										case 'F':
  											s.PushFloat();
  											break;
  										case 'D':
  											s.PushDouble();
  											break;
  										case 'J':
  											s.PushLong();
  											break;
  										case 'L':
  										case '[':
  											throw new VerifyError();
  										default:
  											throw new InvalidOperationException();
  									}
  								}
  								else
  								{
  									ClassFile.ConstantPoolItemFieldref cpi = GetFieldref(instr.Arg1);
  									if(cpi.GetField() != null && cpi.GetField().FieldTypeWrapper.IsUnloadable)
  									{
  										s.PushType(cpi.GetField().FieldTypeWrapper);
  									}
  									else
  									{
  										s.PushType(cpi.GetFieldType());
  									}
  								}
  								break;
  							case NormalizedByteCode.__putstatic:
  							case NormalizedByteCode.__dynamic_putstatic:
  								// special support for when we're being called from IsSideEffectFreeStaticInitializer
  								if(mw == null)
  								{
  									switch(GetFieldref(instr.Arg1).Signature[0])
  									{
  										case 'B':
  										case 'Z':
  										case 'C':
  										case 'S':
  										case 'I':
  											s.PopInt();
  											break;
  										case 'F':
  											s.PopFloat();
  											break;
  										case 'D':
  											s.PopDouble();
  											break;
  										case 'J':
  											s.PopLong();
  											break;
  										case 'L':
  										case '[':
  											if(s.PopAnyType() != VerifierTypeWrapper.Null)
  											{
  												throw new VerifyError();
  											}
  											break;
  										default:
  											throw new InvalidOperationException();
  									}
  								}
  								else
  								{
  									s.PopType(GetFieldref(instr.Arg1).GetFieldType());
  								}
  								break;
  							case NormalizedByteCode.__getfield:
  							case NormalizedByteCode.__dynamic_getfield:
  							{
  								s.PopObjectType(GetFieldref(instr.Arg1).GetClassType());
  								ClassFile.ConstantPoolItemFieldref cpi = GetFieldref(instr.Arg1);
  								if(cpi.GetField() != null && cpi.GetField().FieldTypeWrapper.IsUnloadable)
  								{
  									s.PushType(cpi.GetField().FieldTypeWrapper);
  								}
  								else
  								{
  									s.PushType(cpi.GetFieldType());
  								}
  								break;
  							}
  							case NormalizedByteCode.__putfield:
  							case NormalizedByteCode.__dynamic_putfield:
  								s.PopType(GetFieldref(instr.Arg1).GetFieldType());
  								// putfield is allowed to access the uninitialized this
  								if(s.PeekType() == VerifierTypeWrapper.UninitializedThis
  									&& wrapper.IsAssignableTo(GetFieldref(instr.Arg1).GetClassType()))
  								{
  									s.PopType();
  								}
  								else
  								{
  									s.PopObjectType(GetFieldref(instr.Arg1).GetClassType());
  								}
  								break;
  							case NormalizedByteCode.__ldc:
  							{
  								switch(GetConstantPoolConstantType(instr.Arg1))
  								{
  									case ClassFile.ConstantType.Double:
  										s.PushDouble();
  										break;
  									case ClassFile.ConstantType.Float:
  										s.PushFloat();
  										break;
  									case ClassFile.ConstantType.Integer:
  										s.PushInt();
  										break;
  									case ClassFile.ConstantType.Long:
  										s.PushLong();
  										break;
  									case ClassFile.ConstantType.String:
  										s.PushType(CoreClasses.java.lang.String.Wrapper);
  										break;
  									case ClassFile.ConstantType.Class:
  										if(classFile.MajorVersion < 49)
  										{
  											throw new VerifyError("Illegal type in constant pool");
  										}
  										s.PushType(CoreClasses.java.lang.Class.Wrapper);
  										break;
  									default:
  										// NOTE this is not a VerifyError, because it cannot happen (unless we have
  										// a bug in ClassFile.GetConstantPoolConstantType)
  										throw new InvalidOperationException();
  								}
  								break;
  							}
  							case NormalizedByteCode.__clone_array:
  							case NormalizedByteCode.__invokevirtual:
  							case NormalizedByteCode.__invokespecial:
  							case NormalizedByteCode.__invokeinterface:
  							case NormalizedByteCode.__invokestatic:
  							case NormalizedByteCode.__dynamic_invokevirtual:
  							case NormalizedByteCode.__dynamic_invokespecial:
  							case NormalizedByteCode.__dynamic_invokeinterface:
  							case NormalizedByteCode.__dynamic_invokestatic:
  							{
  								ClassFile.ConstantPoolItemMI cpi = GetMethodref(instr.Arg1);
  								s.MultiPopAnyType(cpi.GetArgTypes().Length);
  								if(instr.NormalizedOpCode != NormalizedByteCode.__invokestatic
  									&& instr.NormalizedOpCode != NormalizedByteCode.__dynamic_invokestatic)
  								{
  									TypeWrapper type = s.PopType();
  									if(ReferenceEquals(cpi.Name, StringConstants.INIT))
  									{
  										// after we've invoked the constructor, the uninitialized references
  										// are now initialized
  										if(type == VerifierTypeWrapper.UninitializedThis)
  										{
  											if(s.GetLocalTypeEx(0) == type)
  											{
  												s.SetLocalType(0, thisType, i);
  											}
  											s.MarkInitialized(type, wrapper, i);
  											s.SetUnitializedThis(false);
  										}
  										else if(VerifierTypeWrapper.IsNew(type))
  										{
  											s.MarkInitialized(type, ((VerifierTypeWrapper)type).UnderlyingType, i);
  										}
  										else
  										{
  											// This is a VerifyError, but it will be caught by our second pass
  										}
  									}
  								}
  								TypeWrapper retType = cpi.GetRetType();
  								if(retType != PrimitiveTypeWrapper.VOID)
  								{
  									if(cpi.GetMethod() != null && cpi.GetMethod().ReturnType.IsUnloadable)
  									{
  										s.PushType(cpi.GetMethod().ReturnType);
  									}
  									else if(retType == PrimitiveTypeWrapper.DOUBLE)
  									{
  										s.PushExtendedDouble();
  									}
  									else if(retType == PrimitiveTypeWrapper.FLOAT)
  									{
  										s.PushExtendedFloat();
  									}
  									else
  									{
  										s.PushType(retType);
  									}
  								}
  								break;
  							}
  							case NormalizedByteCode.__goto:
  								break;
  							case NormalizedByteCode.__istore:
  								s.PopInt();
  								s.SetLocalInt(instr.NormalizedArg1, i);
  								break;
  							case NormalizedByteCode.__iload:
  								s.GetLocalInt(instr.NormalizedArg1);
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__ineg:
  								s.PopInt();
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__iadd:
  							case NormalizedByteCode.__isub:
  							case NormalizedByteCode.__imul:
  							case NormalizedByteCode.__idiv:
  							case NormalizedByteCode.__irem:
  							case NormalizedByteCode.__iand:
  							case NormalizedByteCode.__ior:
  							case NormalizedByteCode.__ixor:
  							case NormalizedByteCode.__ishl:
  							case NormalizedByteCode.__ishr:
  							case NormalizedByteCode.__iushr:
  								s.PopInt();
  								s.PopInt();
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__lneg:
  								s.PopLong();
  								s.PushLong();
  								break;
  							case NormalizedByteCode.__ladd:
  							case NormalizedByteCode.__lsub:
  							case NormalizedByteCode.__lmul:
  							case NormalizedByteCode.__ldiv:
  							case NormalizedByteCode.__lrem:
  							case NormalizedByteCode.__land:
  							case NormalizedByteCode.__lor:
  							case NormalizedByteCode.__lxor:
  								s.PopLong();
  								s.PopLong();
  								s.PushLong();
  								break;
  							case NormalizedByteCode.__lshl:
  							case NormalizedByteCode.__lshr:
  							case NormalizedByteCode.__lushr:
  								s.PopInt();
  								s.PopLong();
  								s.PushLong();
  								break;
  							case NormalizedByteCode.__fneg:
  								if(s.PopFloat())
  								{
  									s.PushExtendedFloat();
  								}
  								else
  								{
  									s.PushFloat();
  								}
  								break;
  							case NormalizedByteCode.__fadd:
  							case NormalizedByteCode.__fsub:
  							case NormalizedByteCode.__fmul:
  							case NormalizedByteCode.__fdiv:
  							case NormalizedByteCode.__frem:
  								s.PopFloat();
  								s.PopFloat();
  								s.PushExtendedFloat();
  								break;
  							case NormalizedByteCode.__dneg:
  								if(s.PopDouble())
  								{
  									s.PushExtendedDouble();
  								}
  								else
  								{
  									s.PushDouble();
  								}
  								break;
  							case NormalizedByteCode.__dadd:
  							case NormalizedByteCode.__dsub:
  							case NormalizedByteCode.__dmul:
  							case NormalizedByteCode.__ddiv:
  							case NormalizedByteCode.__drem:
  								s.PopDouble();
  								s.PopDouble();
  								s.PushExtendedDouble();
  								break;
  							case NormalizedByteCode.__new:
  							{
  								// mark the type, so that we can ascertain that it is a "new object"
  								TypeWrapper type;
  								if(!newTypes.TryGetValue(i, out type))
  								{
  									type = GetConstantPoolClassType(instr.Arg1);
  									if(type.IsArray)
  									{
  										throw new VerifyError("Illegal use of array type");
  									}
  									type = VerifierTypeWrapper.MakeNew(type, i);
  									newTypes[i] = type;
  								}
  								s.PushType(type);
  								break;
  							}
  							case NormalizedByteCode.__multianewarray:
  							{
  								if(instr.Arg2 < 1)
  								{
  									throw new VerifyError("Illegal dimension argument");
  								}
  								for(int j = 0; j < instr.Arg2; j++)
  								{
  									s.PopInt();
  								}
  								TypeWrapper type = GetConstantPoolClassType(instr.Arg1);
  								if(type.ArrayRank < instr.Arg2)
  								{
  									throw new VerifyError("Illegal dimension argument");
  								}
  								s.PushType(type);
  								break;
  							}
  							case NormalizedByteCode.__anewarray:
  							{
  								s.PopInt();
  								TypeWrapper type = GetConstantPoolClassType(instr.Arg1);
  								if(type.IsUnloadable)
  								{
  									s.PushType(new UnloadableTypeWrapper("[" + type.SigName));
  								}
  								else
  								{
  									s.PushType(type.MakeArrayType(1));
  								}
  								break;
  							}
  							case NormalizedByteCode.__newarray:
  								s.PopInt();
  							switch(instr.Arg1)
  							{
  								case 4:
  									s.PushType(BooleanArrayType);
  									break;
  								case 5:
  									s.PushType(CharArrayType);
  									break;
  								case 6:
  									s.PushType(FloatArrayType);
  									break;
  								case 7:
  									s.PushType(DoubleArrayType);
  									break;
  								case 8:
  									s.PushType(ByteArrayType);
  									break;
  								case 9:
  									s.PushType(ShortArrayType);
  									break;
  								case 10:
  									s.PushType(IntArrayType);
  									break;
  								case 11:
  									s.PushType(LongArrayType);
  									break;
  								default:
  									throw new VerifyError("Bad type");
  							}
  								break;
  							case NormalizedByteCode.__swap:
  							{
  								TypeWrapper t1 = s.PopType();
  								TypeWrapper t2 = s.PopType();
  								s.PushType(t1);
  								s.PushType(t2);
  								break;
  							}
  							case NormalizedByteCode.__dup:
  							{
  								TypeWrapper t = s.PopType();
  								s.PushType(t);
  								s.PushType(t);
  								break;
  							}
  							case NormalizedByteCode.__dup2:
  							{
  								TypeWrapper t = s.PopAnyType();
  								if(t.IsWidePrimitive || t == VerifierTypeWrapper.ExtendedDouble)
  								{
  									s.PushType(t);
  									s.PushType(t);
  								}
  								else
  								{
  									TypeWrapper t2 = s.PopType();
  									s.PushType(t2);
  									s.PushType(t);
  									s.PushType(t2);
  									s.PushType(t);
  								}
  								break;
  							}
  							case NormalizedByteCode.__dup_x1:
  							{
  								TypeWrapper value1 = s.PopType();
  								TypeWrapper value2 = s.PopType();
  								s.PushType(value1);
  								s.PushType(value2);
  								s.PushType(value1);
  								break;
  							}
  							case NormalizedByteCode.__dup2_x1:
  							{
  								TypeWrapper value1 = s.PopAnyType();
  								if(value1.IsWidePrimitive || value1 == VerifierTypeWrapper.ExtendedDouble)
  								{
  									TypeWrapper value2 = s.PopType();
  									s.PushType(value1);
  									s.PushType(value2);
  									s.PushType(value1);
  								}
  								else
  								{
  									TypeWrapper value2 = s.PopType();
  									TypeWrapper value3 = s.PopType();
  									s.PushType(value2);
  									s.PushType(value1);
  									s.PushType(value3);
  									s.PushType(value2);
  									s.PushType(value1);
  								}
  								break;
  							}
  							case NormalizedByteCode.__dup_x2:
  							{
  								TypeWrapper value1 = s.PopType();
  								TypeWrapper value2 = s.PopAnyType();
  								if(value2.IsWidePrimitive || value2 == VerifierTypeWrapper.ExtendedDouble)
  								{
  									s.PushType(value1);
  									s.PushType(value2);
  									s.PushType(value1);
  								}
  								else
  								{
  									TypeWrapper value3 = s.PopType();
  									s.PushType(value1);
  									s.PushType(value3);
  									s.PushType(value2);
  									s.PushType(value1);
  								}
  								break;
  							}
  							case NormalizedByteCode.__dup2_x2:
  							{
  								TypeWrapper value1 = s.PopAnyType();
  								if(value1.IsWidePrimitive || value1 == VerifierTypeWrapper.ExtendedDouble)
  								{
  									TypeWrapper value2 = s.PopAnyType();
  									if(value2.IsWidePrimitive || value2 == VerifierTypeWrapper.ExtendedDouble)
  									{
  										// Form 4
  										s.PushType(value1);
  										s.PushType(value2);
  										s.PushType(value1);
  									}
  									else
  									{
  										// Form 2
  										TypeWrapper value3 = s.PopType();
  										s.PushType(value1);
  										s.PushType(value3);
  										s.PushType(value2);
  										s.PushType(value1);
  									}
  								}
  								else
  								{
  									TypeWrapper value2 = s.PopType();
  									TypeWrapper value3 = s.PopAnyType();
  									if(value3.IsWidePrimitive || value3 == VerifierTypeWrapper.ExtendedDouble)
  									{
  										// Form 3
  										s.PushType(value2);
  										s.PushType(value1);
  										s.PushType(value3);
  										s.PushType(value2);
  										s.PushType(value1);
  									}
  									else
  									{
  										// Form 4
  										TypeWrapper value4 = s.PopType();
  										s.PushType(value2);
  										s.PushType(value1);
  										s.PushType(value4);
  										s.PushType(value3);
  										s.PushType(value2);
  										s.PushType(value1);
  									}
  								}
  								break;
  							}
  							case NormalizedByteCode.__pop:
  								s.PopType();
  								break;
  							case NormalizedByteCode.__pop2:
  							{
  								TypeWrapper type = s.PopAnyType();
  								if(!type.IsWidePrimitive && type != VerifierTypeWrapper.ExtendedDouble)
  								{
  									s.PopType();
  								}
  								break;
  							}
  							case NormalizedByteCode.__monitorenter:
  							case NormalizedByteCode.__monitorexit:
  								// TODO these bytecodes are allowed on an uninitialized object, but
  								// we don't support that at the moment...
  								s.PopObjectType();
  								break;
  							case NormalizedByteCode.__return:
  								// mw is null if we're called from IsSideEffectFreeStaticInitializer
  								if(mw != null)
  								{
  									if(mw.ReturnType != PrimitiveTypeWrapper.VOID)
  									{
  										throw new VerifyError("Wrong return type in function");
  									}
  									// if we're a constructor, make sure we called the base class constructor
  									s.CheckUninitializedThis();
  								}
  								break;
  							case NormalizedByteCode.__areturn:
  								s.PopObjectType(mw.ReturnType);
  								break;
  							case NormalizedByteCode.__ireturn:
  							{
  								s.PopInt();
  								if(!mw.ReturnType.IsIntOnStackPrimitive)
  								{
  									throw new VerifyError("Wrong return type in function");
  								}
  								break;
  							}
  							case NormalizedByteCode.__lreturn:
  								s.PopLong();
  								if(mw.ReturnType != PrimitiveTypeWrapper.LONG)
  								{
  									throw new VerifyError("Wrong return type in function");
  								}
  								break;
  							case NormalizedByteCode.__freturn:
  								s.PopFloat();
  								if(mw.ReturnType != PrimitiveTypeWrapper.FLOAT)
  								{
  									throw new VerifyError("Wrong return type in function");
  								}
  								break;
  							case NormalizedByteCode.__dreturn:
  								s.PopDouble();
  								if(mw.ReturnType != PrimitiveTypeWrapper.DOUBLE)
  								{
  									throw new VerifyError("Wrong return type in function");
  								}
  								break;
  							case NormalizedByteCode.__fload:
  								s.GetLocalFloat(instr.NormalizedArg1);
  								s.PushFloat();
  								break;
  							case NormalizedByteCode.__fstore:
  								s.PopFloat();
  								s.SetLocalFloat(instr.NormalizedArg1, i);
  								break;
  							case NormalizedByteCode.__dload:
  								s.GetLocalDouble(instr.NormalizedArg1);
  								s.PushDouble();
  								break;
  							case NormalizedByteCode.__dstore:
  								s.PopDouble();
  								s.SetLocalDouble(instr.NormalizedArg1, i);
  								break;
  							case NormalizedByteCode.__lload:
  								s.GetLocalLong(instr.NormalizedArg1);
  								s.PushLong();
  								break;
  							case NormalizedByteCode.__lstore:
  								s.PopLong();
  								s.SetLocalLong(instr.NormalizedArg1, i);
  								break;
  							case NormalizedByteCode.__lconst_0:
  							case NormalizedByteCode.__lconst_1:
  								s.PushLong();
  								break;
  							case NormalizedByteCode.__fconst_0:
  							case NormalizedByteCode.__fconst_1:
  							case NormalizedByteCode.__fconst_2:
  								s.PushFloat();
  								break;
  							case NormalizedByteCode.__dconst_0:
  							case NormalizedByteCode.__dconst_1:
  								s.PushDouble();
  								break;
  							case NormalizedByteCode.__lcmp:
  								s.PopLong();
  								s.PopLong();
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__fcmpl:
  							case NormalizedByteCode.__fcmpg:
  								s.PopFloat();
  								s.PopFloat();
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__dcmpl:
  							case NormalizedByteCode.__dcmpg:
  								s.PopDouble();
  								s.PopDouble();
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__checkcast:
  								s.PopObjectType();
  								s.PushType(GetConstantPoolClassType(instr.Arg1));
  								break;
  							case NormalizedByteCode.__instanceof:
  								s.PopObjectType();
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__iinc:
  								s.GetLocalInt(instr.Arg1);
  								break;
  							case NormalizedByteCode.__athrow:
  								if (VerifierTypeWrapper.IsFaultBlockException(s.PeekType()))
  								{
  									s.PopFaultBlockException();
  								}
  								else
  								{
  									s.PopObjectType(CoreClasses.java.lang.Throwable.Wrapper);
  								}
  								break;
  							case NormalizedByteCode.__tableswitch:
  							case NormalizedByteCode.__lookupswitch:
  								s.PopInt();
  								break;
  							case NormalizedByteCode.__i2b:
  								s.PopInt();
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__i2c:
  								s.PopInt();
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__i2s:
  								s.PopInt();
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__i2l:
  								s.PopInt();
  								s.PushLong();
  								break;
  							case NormalizedByteCode.__i2f:
  								s.PopInt();
  								s.PushFloat();
  								break;
  							case NormalizedByteCode.__i2d:
  								s.PopInt();
  								s.PushDouble();
  								break;
  							case NormalizedByteCode.__l2i:
  								s.PopLong();
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__l2f:
  								s.PopLong();
  								s.PushFloat();
  								break;
  							case NormalizedByteCode.__l2d:
  								s.PopLong();
  								s.PushDouble();
  								break;
  							case NormalizedByteCode.__f2i:
  								s.PopFloat();
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__f2l:
  								s.PopFloat();
  								s.PushLong();
  								break;
  							case NormalizedByteCode.__f2d:
  								s.PopFloat();
  								s.PushDouble();
  								break;
  							case NormalizedByteCode.__d2i:
  								s.PopDouble();
  								s.PushInt();
  								break;
  							case NormalizedByteCode.__d2f:
  								s.PopDouble();
  								s.PushFloat();
  								break;
  							case NormalizedByteCode.__d2l:
  								s.PopDouble();
  								s.PushLong();
  								break;
  							case NormalizedByteCode.__nop:
  								if(i + 1 == instructions.Length)
  								{
  									throw new VerifyError("Falling off the end of the code");
  								}
  								break;
  							case NormalizedByteCode.__static_error:
  								break;
  							default:
  								throw new NotImplementedException(instr.NormalizedOpCode.ToString());
  						}
  						if(s.GetStackHeight() > method.MaxStack)
  						{
  							throw new VerifyError("Stack size too large");
  						}
  						try
  						{
  							switch(ByteCodeMetaData.GetFlowControl(instr.NormalizedOpCode))
  							{
  								case ByteCodeFlowControl.Switch:
  									for(int j = 0; j < instr.SwitchEntryCount; j++)
  									{
  										state[instr.GetSwitchTargetIndex(j)] += s;
  									}
  									state[instr.DefaultTarget] += s;
  									break;
  								case ByteCodeFlowControl.CondBranch:
  									state[i + 1] += s;
  									state[instr.TargetIndex] += s;
  									break;
  								case ByteCodeFlowControl.Branch:
  									state[instr.TargetIndex] += s;
  									break;
  								case ByteCodeFlowControl.Return:
  								case ByteCodeFlowControl.Throw:
  									break;
  								case ByteCodeFlowControl.Next:
  									state[i + 1] += s;
  									break;
  								default:
  									throw new InvalidOperationException();
  							}
  						}
  						catch(IndexOutOfRangeException)
  						{
  							// we're going to assume that this always means that we have an invalid branch target
  							// NOTE because PcIndexMap returns -1 for illegal PCs (in the middle of an instruction) and
  							// we always use that value as an index into the state array, any invalid PC will result
  							// in an IndexOutOfRangeException
  							throw new VerifyError("Illegal target of jump or branch");
  						}
  					}
  					catch(VerifyError x)
  					{
  						string opcode = instructions[i].NormalizedOpCode.ToString();
  						if(opcode.StartsWith("__"))
  						{
  							opcode = opcode.Substring(2);
  						}
  						throw new VerifyError(string.Format("{5} (class: {0}, method: {1}, signature: {2}, offset: {3}, instruction: {4})",
  							classFile.Name, method.Name, method.Signature, instructions[i].PC, opcode, x.Message), x);
  					}
  				}
  			}
  		}
  	}
  
  	// this verification pass must run on the unmodified bytecode stream
  	private void VerifyPassTwo()
  	{
  		ClassFile.Method.Instruction[] instructions = method.Instructions;
  		for (int i = 0; i < instructions.Length; i++)
  		{
  			if (state[i] != null)
  			{
  				try
  				{
  					switch (instructions[i].NormalizedOpCode)
  					{
  						case NormalizedByteCode.__invokeinterface:
  						case NormalizedByteCode.__invokespecial:
  						case NormalizedByteCode.__invokestatic:
  						case NormalizedByteCode.__invokevirtual:
  							VerifyInvokePassTwo(i);
  							break;
  					}
  					// verify backward branches
  					switch (ByteCodeMetaData.GetFlowControl(instructions[i].NormalizedOpCode))
  					{
  						case ByteCodeFlowControl.Switch:
  							{
  								bool hasbackbranch = false;
  								for (int j = 0; j < instructions[i].SwitchEntryCount; j++)
  								{
  									hasbackbranch |= instructions[i].GetSwitchTargetIndex(j) < i;
  								}
  								hasbackbranch |= instructions[i].DefaultTarget < i;
  								if (hasbackbranch)
  								{
  									// backward branches cannot have uninitialized objects on
  									// the stack or in local variables
  									state[i].CheckUninitializedObjRefs();
  								}
  								break;
  							}
  						case ByteCodeFlowControl.Branch:
  						case ByteCodeFlowControl.CondBranch:
  							if (instructions[i].TargetIndex < i)
  							{
  								// backward branches cannot have uninitialized objects on
  								// the stack or in local variables
  								state[i].CheckUninitializedObjRefs();
  							}
  							break;
  					}
  				}
  				catch (VerifyError x)
  				{
  					string opcode = instructions[i].NormalizedOpCode.ToString();
  					if (opcode.StartsWith("__"))
  					{
  						opcode = opcode.Substring(2);
  					}
  					throw new VerifyError(string.Format("{5} (class: {0}, method: {1}, signature: {2}, offset: {3}, instruction: {4})",
  						classFile.Name, method.Name, method.Signature, instructions[i].PC, opcode, x.Message), x);
  				}
  			}
  		}
  	}
  
  	private void VerifyInvokePassTwo(int index)
  	{
  		StackState stack = new StackState(state[index]);
  		NormalizedByteCode invoke = method.Instructions[index].NormalizedOpCode;
  		ClassFile.ConstantPoolItemMI cpi = GetMethodref(method.Instructions[index].Arg1);
  		if ((cpi is ClassFile.ConstantPoolItemInterfaceMethodref) != (invoke == NormalizedByteCode.__invokeinterface))
  		{
  			throw new VerifyError("Illegal constant pool index");
  		}
  		if (invoke != NormalizedByteCode.__invokespecial && ReferenceEquals(cpi.Name, StringConstants.INIT))
  		{
  			throw new VerifyError("Must call initializers using invokespecial");
  		}
  		if (ReferenceEquals(cpi.Name, StringConstants.CLINIT))
  		{
  			throw new VerifyError("Illegal call to internal method");
  		}
  		TypeWrapper[] args = cpi.GetArgTypes();
  		for (int j = args.Length - 1; j >= 0; j--)
  		{
  			stack.PopType(args[j]);
  		}
  		if (invoke == NormalizedByteCode.__invokeinterface)
  		{
  			int argcount = args.Length + 1;
  			for (int j = 0; j < args.Length; j++)
  			{
  				if (args[j].IsWidePrimitive)
  				{
  					argcount++;
  				}
  			}
  			if (method.Instructions[index].Arg2 != argcount)
  			{
  				throw new VerifyError("Inconsistent args size");
  			}
  		}
  		bool isnew = false;
  		TypeWrapper thisType;
  		if (invoke == NormalizedByteCode.__invokestatic)
  		{
  			thisType = null;
  		}
  		else
  		{
  			thisType = SigTypeToClassName(stack.PeekType(), cpi.GetClassType(), wrapper);
  			if (ReferenceEquals(cpi.Name, StringConstants.INIT))
  			{
  				TypeWrapper type = stack.PopType();
  				isnew = VerifierTypeWrapper.IsNew(type);
  				if ((isnew && ((VerifierTypeWrapper)type).UnderlyingType != cpi.GetClassType()) ||
  					(type == VerifierTypeWrapper.UninitializedThis && cpi.GetClassType() != wrapper.BaseTypeWrapper && cpi.GetClassType() != wrapper) ||
  					(!isnew && type != VerifierTypeWrapper.UninitializedThis))
  				{
  					// TODO oddly enough, Java fails verification for the class without
  					// even running the constructor, so maybe constructors are always
  					// verified...
  					// NOTE when a constructor isn't verifiable, the static initializer
  					// doesn't run either
  					throw new VerifyError("Call to wrong initialization method");
  				}
  			}
  			else
  			{
  				if (invoke != NormalizedByteCode.__invokeinterface)
  				{
  
gt: 					TypeWrapper refType = stack.PopObjectType();