/*
  Copyright (C) 2002-2014 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.Collections.Generic;
#if STATIC_COMPILER || STUB_GENERATOR
using IKVM.Reflection;
using IKVM.Reflection.Emit;
using Type = IKVM.Reflection.Type;
#else
using System.Reflection;
using System.Reflection.Emit;
#endif
using System.Diagnostics;
using IKVM.Attributes;
using System.Threading;
using System.Runtime.InteropServices;

namespace IKVM.Internal
{
	[Flags]
	enum MemberFlags : short
	{
		None = 0,
		HideFromReflection = 1,
		ExplicitOverride = 2,
		MirandaMethod = 8,
		AccessStub = 16,
		InternalAccess = 32,  // member has "internal" access (@ikvm.lang.Internal)
		PropertyAccessor = 64,
		Intrinsic = 128,
		CallerID = 256,
		NonPublicTypeInSignature = 512,	// this flag is only available after linking and is not set for access stubs
		DelegateInvokeWithByRefParameter = 1024,
		Type2FinalField = 2048,
	}

	abstract class MemberWrapper
	{
		private HandleWrapper handle;
		private readonly TypeWrapper declaringType;
		protected readonly Modifiers modifiers;
		private MemberFlags flags;
		private readonly string name;
		private readonly string sig;

		private sealed class HandleWrapper
		{
			internal readonly IntPtr Value;

			[System.Security.SecurityCritical]
			internal HandleWrapper(MemberWrapper obj)
			{
				Value = (IntPtr)GCHandle.Alloc(obj, GCHandleType.WeakTrackResurrection);
			}

#if CLASSGC
			[System.Security.SecuritySafeCritical]
			~HandleWrapper()
			{
				if (!Environment.HasShutdownStarted)
				{
					GCHandle h = (GCHandle)Value;
					if (h.Target == null)
					{
						h.Free();
					}
					else
					{
						GC.ReRegisterForFinalize(this);
					}
				}
			}
#endif
		}

		protected MemberWrapper(TypeWrapper declaringType, string name, string sig, Modifiers modifiers, MemberFlags flags)
		{
			Debug.Assert(declaringType != null);
			this.declaringType = declaringType;
			this.name = String.Intern(name);
			this.sig = String.Intern(sig);
			this.modifiers = modifiers;
			this.flags = flags;
		}

		internal IntPtr Cookie
		{
			[System.Security.SecurityCritical]
			get
			{
				lock(this)
				{
					if(handle == null)
					{
						handle = new HandleWrapper(this);
					}
				}
				return handle.Value;
			}
		}

		[System.Security.SecurityCritical]
		internal static MemberWrapper FromCookieImpl(IntPtr cookie)
		{
			return (MemberWrapper)GCHandle.FromIntPtr(cookie).Target;
		}

		internal TypeWrapper DeclaringType
		{
			get
			{
				return declaringType;
			}
		}

		internal string Name
		{
			get
			{
				return name;
			}
		}

		internal string Signature
		{
			get
			{
				return sig;
			}
		}

		internal bool IsAccessibleFrom(TypeWrapper referencedType, TypeWrapper caller, TypeWrapper instance)
		{
			if(referencedType.IsAccessibleFrom(caller))
			{
				return (
					caller == DeclaringType ||
					IsPublicOrProtectedMemberAccessible(caller, instance) ||
					(IsInternal && DeclaringType.InternalsVisibleTo(caller)) ||
					(!IsPrivate && DeclaringType.IsPackageAccessibleFrom(caller)))
					// The JVM supports accessing members that have non-public types in their signature from another package,
					// but the CLI doesn't. It would be nice if we worked around that by emitting extra accessors, but for now
					// we'll simply disallow such access across assemblies (unless the appropriate InternalsVisibleToAttribute exists).
					&& (!(HasNonPublicTypeInSignature || IsType2FinalField) || InPracticeInternalsVisibleTo(caller));
			}
			return false;
		}

		private bool IsPublicOrProtectedMemberAccessible(TypeWrapper caller, TypeWrapper instance)
		{
			if (IsPublic || (IsProtected && caller.IsSubTypeOf(DeclaringType) && (IsStatic || instance.IsUnloadable || instance.IsSubTypeOf(caller))))
			{
				return DeclaringType.IsPublic || InPracticeInternalsVisibleTo(caller);
			}
			return false;
		}

		private bool InPracticeInternalsVisibleTo(TypeWrapper caller)
		{
#if !STATIC_COMPILER
			if (DeclaringType.TypeAsTBD.Assembly.Equals(caller.TypeAsTBD.Assembly))
			{
				// both the caller and the declaring type are in the same assembly
				// so we know that the internals are visible
				// (this handles the case where we're running in dynamic mode)
				return true;
			}
#endif
#if CLASSGC
			if (DeclaringType.IsDynamic)
			{
				// if we are dynamic, we can just become friends with the caller
				DeclaringType.GetClassLoader().GetTypeWrapperFactory().AddInternalsVisibleTo(caller.TypeAsTBD.Assembly);
				return true;
			}
#endif
			return DeclaringType.InternalsVisibleTo(caller);
		}

		internal bool IsHideFromReflection
		{
			get
			{
				return (flags & MemberFlags.HideFromReflection) != 0;
			}
		}

		internal bool IsExplicitOverride
		{
			get
			{
				return (flags & MemberFlags.ExplicitOverride) != 0;
			}
		}

		internal bool IsMirandaMethod
		{
			get
			{
				return (flags & MemberFlags.MirandaMethod) != 0;
			}
		}

		internal bool IsAccessStub
		{
			get
			{
				return (flags & MemberFlags.AccessStub) != 0;
			}
		}

		internal bool IsPropertyAccessor
		{
			get
			{
				return (flags & MemberFlags.PropertyAccessor) != 0;
			}
			set
			{
				// this is unsynchronized, so it may only be called during the JavaTypeImpl constructor
				if(value)
				{
					flags |= MemberFlags.PropertyAccessor;
				}
				else
				{
					flags &= ~MemberFlags.PropertyAccessor;
				}
			}
		}

		internal bool IsIntrinsic
		{
			get
			{
				return (flags & MemberFlags.Intrinsic) != 0;
			}
		}

		protected void SetIntrinsicFlag()
		{
			flags |= MemberFlags.Intrinsic;
		}

		protected void SetNonPublicTypeInSignatureFlag()
		{
			flags |= MemberFlags.NonPublicTypeInSignature;
		}

		internal bool HasNonPublicTypeInSignature
		{
			get { return (flags & MemberFlags.NonPublicTypeInSignature) != 0; }
		}

		protected void SetType2FinalField()
		{
			flags |= MemberFlags.Type2FinalField;
		}
	
		internal bool IsType2FinalField
		{
			get { return (flags & MemberFlags.Type2FinalField) != 0; }
		}

		internal bool HasCallerID
		{
			get
			{
				return (flags & MemberFlags.CallerID) != 0;
			}
		}

		internal bool IsDelegateInvokeWithByRefParameter
		{
			get { return (flags & MemberFlags.DelegateInvokeWithByRefParameter) != 0; }
		}

		internal Modifiers Modifiers
		{
			get
			{
				return modifiers;
			}
		}

		internal bool IsStatic
		{
			get
			{
				return (modifiers & Modifiers.Static) != 0;
			}
		}

		internal bool IsInternal
		{
			get
			{
				return (flags & MemberFlags.InternalAccess) != 0;
			}
		}

		internal bool IsPublic
		{
			get
			{
				return (modifiers & Modifiers.Public) != 0;
			}
		}

		internal bool IsPrivate
		{
			get
			{
				return (modifiers & Modifiers.Private) != 0;
			}
		}

		internal bool IsProtected
		{
			get
			{
				return (modifiers & Modifiers.Protected) != 0;
			}
		}

		internal bool IsFinal
		{
			get
			{
				return (modifiers & Modifiers.Final) != 0;
			}
		}
	}

	abstract class MethodWrapper : MemberWrapper
	{
#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR
		private volatile java.lang.reflect.Executable reflectionMethod;
#endif
		internal static readonly MethodWrapper[] EmptyArray  = new MethodWrapper[0];
		private MethodBase method;
		private string[] declaredExceptions;
		private TypeWrapper returnTypeWrapper;
		private TypeWrapper[] parameterTypeWrappers;

#if EMITTERS
		internal virtual void EmitCall(CodeEmitter ilgen)
		{
			throw new InvalidOperationException();
		}

		internal virtual void EmitCallvirt(CodeEmitter ilgen)
		{
			throw new InvalidOperationException();
		}

		internal virtual void EmitCallvirtReflect(CodeEmitter ilgen)
		{
			EmitCallvirt(ilgen);
		}

		internal virtual void EmitNewobj(CodeEmitter ilgen)
		{
			throw new InvalidOperationException();
		}

		internal virtual bool EmitIntrinsic(EmitIntrinsicContext context)
		{
			return Intrinsics.Emit(context);
		}
#endif // EMITTERS

		internal virtual bool IsDynamicOnly
		{
			get
			{
				return false;
			}
		}

		internal MethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags)
			: base(declaringType, name, sig, modifiers, flags)
		{
			Profiler.Count("MethodWrapper");
			this.method = method;
			Debug.Assert(((returnType == null) == (parameterTypes == null)) || (returnType == PrimitiveTypeWrapper.VOID));
			this.returnTypeWrapper = returnType;
			this.parameterTypeWrappers = parameterTypes;
			if (Intrinsics.IsIntrinsic(this))
			{
				SetIntrinsicFlag();
			}
			UpdateNonPublicTypeInSignatureFlag();
		}

		private void UpdateNonPublicTypeInSignatureFlag()
		{
			if ((IsPublic || IsProtected) && (returnTypeWrapper != null && parameterTypeWrappers != null) && !(this is AccessStubMethodWrapper) && !(this is AccessStubConstructorMethodWrapper))
			{
				if (!returnTypeWrapper.IsPublic && !returnTypeWrapper.IsUnloadable)
				{
					SetNonPublicTypeInSignatureFlag();
				}
				else
				{
					foreach (TypeWrapper tw in parameterTypeWrappers)
					{
						if (!tw.IsPublic && !tw.IsUnloadable)
						{
							SetNonPublicTypeInSignatureFlag();
							break;
						}
					}
				}
			}
		}

		internal void SetDeclaredExceptions(string[] exceptions)
		{
			if(exceptions == null)
			{
				exceptions = new string[0];
			}
			this.declaredExceptions = (string[])exceptions.Clone();
		}

		internal string[] GetDeclaredExceptions()
		{
			return declaredExceptions;
		}

#if !STATIC_COMPILER && !STUB_GENERATOR
		internal java.lang.reflect.Executable ToMethodOrConstructor(bool copy)
		{
#if FIRST_PASS
			return null;
#else
			java.lang.reflect.Executable method = reflectionMethod;
			if (method == null)
			{
				Link();
				ClassLoaderWrapper loader = this.DeclaringType.GetClassLoader();
				TypeWrapper[] argTypes = GetParameters();
				java.lang.Class[] parameterTypes = new java.lang.Class[argTypes.Length];
				for (int i = 0; i < argTypes.Length; i++)
				{
					parameterTypes[i] = argTypes[i].EnsureLoadable(loader).ClassObject;
				}
				java.lang.Class[] checkedExceptions = GetExceptions();
				if (this.IsConstructor)
				{
					method = new java.lang.reflect.Constructor(
						this.DeclaringType.ClassObject,
						parameterTypes,
						checkedExceptions,
						(int)this.Modifiers | (this.IsInternal ? 0x40000000 : 0),
						Array.IndexOf(this.DeclaringType.GetMethods(), this),
						this.DeclaringType.GetGenericMethodSignature(this),
						null,
						null
					);
				}
				else
				{
					method = new java.lang.reflect.Method(
						this.DeclaringType.ClassObject,
						this.Name,
						parameterTypes,
						this.ReturnType.EnsureLoadable(loader).ClassObject,
						checkedExceptions,
						(int)this.Modifiers | (this.IsInternal ? 0x40000000 : 0),
						Array.IndexOf(this.DeclaringType.GetMethods(), this),
						this.DeclaringType.GetGenericMethodSignature(this),
						null,
						null,
						null
					);
				}
				lock (this)
				{
					if (reflectionMethod == null)
					{
						reflectionMethod = method;
					}
					else
					{
						method = reflectionMethod;
					}
				}
			}
			if (copy)
			{
				java.lang.reflect.Constructor ctor = method as java.lang.reflect.Constructor;
				if (ctor != null)
				{
					return ctor.copy();
				}
				return ((java.lang.reflect.Method)method).copy();
			}
			return method;
#endif
		}

#if !FIRST_PASS
		private java.lang.Class[] GetExceptions()
		{
			string[] classes = declaredExceptions;
			Type[] types = Type.EmptyTypes;
			if (classes == null)
			{
				// NOTE if method is a MethodBuilder, GetCustomAttributes doesn't work (and if
				// the method had any declared exceptions, the declaredExceptions field would have
				// been set)
				if (method != null && !(method is MethodBuilder))
				{
					ThrowsAttribute attr = AttributeHelper.GetThrows(method);
					if (attr != null)
					{
						classes = attr.classes;
						types = attr.types;
					}
				}
			}
			if (classes != null)
			{
				java.lang.Class[] array = new java.lang.Class[classes.Length];
				for (int i = 0; i < classes.Length; i++)
				{
					array[i] = this.DeclaringType.GetClassLoader().LoadClassByDottedName(classes[i]).ClassObject;
				}
				return array;
			}
			else
			{
				java.lang.Class[] array = new java.lang.Class[types.Length];
				for (int i = 0; i < types.Length; i++)
				{
					array[i] = types[i];
				}
				return array;
			}
		}
#endif // !FIRST_PASS

		internal static MethodWrapper FromExecutable(java.lang.reflect.Executable executable)
		{
#if FIRST_PASS
			return null;
#else
			return TypeWrapper.FromClass(executable.getDeclaringClass()).GetMethods()[executable._slot()];
#endif
		}
#endif // !STATIC_COMPILER && !STUB_GENERATOR

		[System.Security.SecurityCritical]
		internal static MethodWrapper FromCookie(IntPtr cookie)
		{
			return (MethodWrapper)FromCookieImpl(cookie);
		}

		internal bool IsLinked
		{
			get
			{
				return parameterTypeWrappers != null;
			}
		}

		internal void Link()
		{
			lock(this)
			{
				if(parameterTypeWrappers != null)
				{
					return;
				}
			}
			ClassLoaderWrapper loader = this.DeclaringType.GetClassLoader();
			TypeWrapper ret = loader.RetTypeWrapperFromSigNoThrow(Signature);
			TypeWrapper[] parameters = loader.ArgTypeWrapperListFromSigNoThrow(Signature);
			lock(this)
			{
				try
				{
					// critical code in the finally block to avoid Thread.Abort interrupting the thread
				}
				finally
				{
					if(parameterTypeWrappers == null)
					{
						Debug.Assert(returnTypeWrapper == null || returnTypeWrapper == PrimitiveTypeWrapper.VOID);
						returnTypeWrapper = ret;
						parameterTypeWrappers = parameters;
						UpdateNonPublicTypeInSignatureFlag();
						if(method == null)
						{
							try
							{
								DoLinkMethod();
							}
							catch
							{
								// HACK if linking fails, we unlink to make sure
								// that the next link attempt will fail again
								returnTypeWrapper = null;
								parameterTypeWrappers = null;
								throw;
							}
						}
					}
				}
			}
		}

		protected virtual void DoLinkMethod()
		{
			method = this.DeclaringType.LinkMethod(this);
		}

		[Conditional("DEBUG")]
		internal void AssertLinked()
		{
			if(!(parameterTypeWrappers != null && returnTypeWrapper != null))
			{
				Tracer.Error(Tracer.Runtime, "AssertLinked failed: " + this.DeclaringType.Name + "::" + this.Name + this.Signature);
			}
			Debug.Assert(parameterTypeWrappers != null && returnTypeWrapper != null, this.DeclaringType.Name + "::" + this.Name + this.Signature);
		}

		internal TypeWrapper ReturnType
		{
			get
			{
				AssertLinked();
				return returnTypeWrapper;
			}
		}

		internal TypeWrapper[] GetParameters()
		{
			AssertLinked();
			return parameterTypeWrappers;
		}

#if !STUB_GENERATOR
		internal DefineMethodHelper GetDefineMethodHelper()
		{
			return new DefineMethodHelper(this);
		}
#endif

		internal Type ReturnTypeForDefineMethod
		{
			get
			{
				return ReturnType.TypeAsSignatureType;
			}
		}

		internal Type[] GetParametersForDefineMethod()
		{
			TypeWrapper[] wrappers = GetParameters();
			int len = wrappers.Length;
			if(HasCallerID)
			{
				len++;
			}
			Type[] temp = new Type[len];
			for(int i = 0; i < wrappers.Length; i++)
			{
				temp[i] = wrappers[i].TypeAsSignatureType;
			}
			if(HasCallerID)
			{
				temp[len - 1] = CoreClasses.ikvm.@internal.CallerID.Wrapper.TypeAsSignatureType;
			}
			return temp;
		}

		// we expose the underlying MethodBase object,
		// for Java types, this is the method that contains the compiled Java bytecode
		// for remapped types, this is the mbCore method (not the helper)
		// Note that for some artificial methods (notably wrap() in enums), method is null
		internal MethodBase GetMethod()
		{
			AssertLinked();
			return method;
		}

		internal string RealName
		{
			get
			{
				AssertLinked();
				return method.Name;
			}
		}

		internal bool IsAbstract
		{
			get
			{
				return (Modifiers & Modifiers.Abstract) != 0;
			}
		}

		internal bool RequiresNonVirtualDispatcher
		{
			get
			{
				return !IsConstructor
					&& !IsStatic
					&& !IsPrivate
					&& !IsAbstract
					&& !IsFinal
					&& !DeclaringType.IsFinal;
			}
		}

#if !STATIC_COMPILER && !STUB_GENERATOR
		internal Type GetDelegateType()
		{
			TypeWrapper[] paramTypes = GetParameters();
			if (paramTypes.Length > MethodHandleUtil.MaxArity)
			{
				Type type = DeclaringType.TypeAsBaseType.Assembly.GetType(
					ReturnType == PrimitiveTypeWrapper.VOID ? "__<>NVIV`" + paramTypes.Length : "__<>NVI`" + (paramTypes.Length + 1));
				if (type == null)
				{
					type = DeclaringType.GetClassLoader().GetTypeWrapperFactory().DefineDelegate(paramTypes.Length, ReturnType == PrimitiveTypeWrapper.VOID);
				}
				Type[] types = new Type[paramTypes.Length + (ReturnType == PrimitiveTypeWrapper.VOID ? 0 : 1)];
				for (int i = 0; i < paramTypes.Length; i++)
				{
					types[i] = paramTypes[i].TypeAsSignatureType;
				}
				if (ReturnType != PrimitiveTypeWrapper.VOID)
				{
					types[types.Length - 1] = ReturnType.TypeAsSignatureType;
				}
				return type.MakeGenericType(types);
			}
			return MethodHandleUtil.CreateMemberWrapperDelegateType(paramTypes, ReturnType);
		}

		internal void ResolveMethod()
		{
#if !FIRST_PASS
			// if we've still got the builder object, we need to replace it with the real thing before we can call it
			MethodBuilder mb = method as MethodBuilder;
			if (mb != null)
			{
				method = mb.Module.ResolveMethod(mb.GetToken().Token);
			}
#endif
		}

		[HideFromJava]
		internal virtual object InvokeNonvirtualRemapped(object obj, object[] args)
		{
			throw new InvalidOperationException();
		}

		[HideFromJava]
		protected static object InvokeAndUnwrapException(MethodBase mb, object obj, object[] args)
		{
#if FIRST_PASS
			return null;
#else
			try
			{
				return mb.Invoke(obj, args);
			}
			catch (TargetInvocationException x)
			{
				throw ikvm.runtime.Util.mapException(x.InnerException);
			}
#endif
		}

		[HideFromJava]
		internal virtual object Invoke(object obj, object[] args)
		{
			return InvokeAndUnwrapException(method, obj, args);
		}

		[HideFromJava]
		internal virtual object CreateInstance(object[] args)
		{
#if FIRST_PASS
			return null;
#else
			try
			{
				return ((ConstructorInfo)method).Invoke(args);
			}
			catch (TargetInvocationException x)
			{
				throw ikvm.runtime.Util.mapException(x.InnerException);
			}
#endif
		}
#endif // !STATIC_COMPILER && !STUB_GENERATOR

		internal static OpCode SimpleOpCodeToOpCode(SimpleOpCode opc)
		{
			switch(opc)
			{
				case SimpleOpCode.Call:
					return OpCodes.Call;
				case SimpleOpCode.Callvirt:
					return OpCodes.Callvirt;
				case SimpleOpCode.Newobj:
					return OpCodes.Newobj;
				default:
					throw new InvalidOperationException();
			}
		}

		internal virtual bool IsOptionalAttributeAnnotationValue
		{
			get { return false; }
		}

		internal bool IsConstructor
		{
			get { return (object)Name == (object)StringConstants.INIT; }
		}

		internal bool IsVirtual
		{
			get
			{
				return (modifiers & (Modifiers.Static | Modifiers.Private)) == 0
					&& !IsConstructor;
			}
		}
	}

	// placeholder for <clinit> method that exist in ClassFile but not in TypeWrapper
	// (because it is optimized away)
	sealed class DummyMethodWrapper : MethodWrapper
	{
		internal DummyMethodWrapper(TypeWrapper tw)
			: base(tw, StringConstants.CLINIT, StringConstants.SIG_VOID, null, PrimitiveTypeWrapper.VOID, TypeWrapper.EmptyArray, Modifiers.Static, MemberFlags.None)
		{
		}

		protected override void DoLinkMethod()
		{
			// we're pre-linked (because we pass the signature types to the base constructor)
			throw new InvalidOperationException();
		}
	}

	abstract class SmartMethodWrapper : MethodWrapper
	{
		internal SmartMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags)
			: base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags)
		{
		}

#if EMITTERS
		internal sealed override void EmitCall(CodeEmitter ilgen)
		{
			AssertLinked();
			CallImpl(ilgen);
		}

		protected virtual void CallImpl(CodeEmitter ilgen)
		{
			throw new InvalidOperationException();
		}

		internal sealed override void EmitCallvirt(CodeEmitter ilgen)
		{
			AssertLinked();
			if(DeclaringType.IsNonPrimitiveValueType)
			{
				// callvirt isn't allowed on a value type
				// (we don't need to check for a null reference, because we're always dealing with an unboxed value)
				CallImpl(ilgen);
			}
			else
			{
				CallvirtImpl(ilgen);
			}
		}

		protected virtual void CallvirtImpl(CodeEmitter ilgen)
		{
			throw new InvalidOperationException();
		}

		internal sealed override void EmitNewobj(CodeEmitter ilgen)
		{
			AssertLinked();
			NewobjImpl(ilgen);
			if(DeclaringType.IsNonPrimitiveValueType)
			{
				DeclaringType.EmitBox(ilgen);
			}
		}

		protected virtual void NewobjImpl(CodeEmitter ilgen)
		{
			throw new InvalidOperationException();
		}
#endif // EMITTERS
	}

	enum SimpleOpCode : byte
	{
		Call,
		Callvirt,
		Newobj
	}

	sealed class SimpleCallMethodWrapper : MethodWrapper
	{
		private readonly SimpleOpCode call;
		private readonly SimpleOpCode callvirt;

		internal SimpleCallMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodInfo method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags, SimpleOpCode call, SimpleOpCode callvirt)
			: base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags)
		{
			this.call = call;
			this.callvirt = callvirt;
		}

#if EMITTERS
		internal override void EmitCall(CodeEmitter ilgen)
		{
			ilgen.Emit(SimpleOpCodeToOpCode(call), GetMethod());
		}

		internal override void EmitCallvirt(CodeEmitter ilgen)
		{
			ilgen.Emit(SimpleOpCodeToOpCode(callvirt), GetMethod());
		}
#endif // EMITTERS
	}

	sealed class TypicalMethodWrapper : SmartMethodWrapper
	{
		internal TypicalMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags)
			: base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags)
		{
		}

#if EMITTERS
		protected override void CallImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Call, GetMethod());
		}

		protected override void CallvirtImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Callvirt, GetMethod());
		}

		protected override void NewobjImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Newobj, GetMethod());
		}
#endif // EMITTERS
	}

	abstract class MirandaMethodWrapper : SmartMethodWrapper
	{
		private readonly MethodWrapper ifmethod;

		private MirandaMethodWrapper(TypeWrapper declaringType, MethodWrapper ifmethod, Modifiers modifiers)
			: base(declaringType, ifmethod.Name, ifmethod.Signature, null, null, null, modifiers, MemberFlags.HideFromReflection | MemberFlags.MirandaMethod)
		{
			this.ifmethod = ifmethod;
		}

		private sealed class AbstractMirandaMethodWrapper : MirandaMethodWrapper
		{
			internal AbstractMirandaMethodWrapper(TypeWrapper declaringType, MethodWrapper ifmethod)
				: base(declaringType, ifmethod, Modifiers.Public | Modifiers.Abstract)
			{
			}
		}

		private sealed class DefaultMirandaMethodWrapper : MirandaMethodWrapper
		{
			internal DefaultMirandaMethodWrapper(TypeWrapper declaringType, MethodWrapper ifmethod)
				: base(declaringType, ifmethod, Modifiers.Public)
			{
			}
		}

		private sealed class ErrorMirandaMethodWrapper : MirandaMethodWrapper
		{
			private string error;

			internal ErrorMirandaMethodWrapper(TypeWrapper declaringType, MethodWrapper ifmethod, string error)
				: base(declaringType, ifmethod, Modifiers.Public)
			{
				this.error = error;
			}

			protected override MirandaMethodWrapper AddConflictError(MethodWrapper mw)
			{
				error += " " + mw.DeclaringType.Name + "." + mw.Name;
				return this;
			}

			internal override string Error
			{
				get { return error; }
			}
		}

		internal static MirandaMethodWrapper Create(TypeWrapper declaringType, MethodWrapper ifmethod)
		{
			DefaultMirandaMethodWrapper dmmw = ifmethod as DefaultMirandaMethodWrapper;
			if (dmmw != null)
			{
				return new DefaultMirandaMethodWrapper(declaringType, dmmw.BaseMethod);
			}
			return ifmethod.IsAbstract
				? (MirandaMethodWrapper)new AbstractMirandaMethodWrapper(declaringType, ifmethod)
				: (MirandaMethodWrapper)new DefaultMirandaMethodWrapper(declaringType, ifmethod);
		}

		internal MirandaMethodWrapper Update(MethodWrapper mw)
		{
			if (ifmethod == mw)
			{
				// an interface can be implemented multiple times
				return this;
			}
			else if (ifmethod.DeclaringType.ImplementsInterface(mw.DeclaringType))
			{
				// we can override a base interface without problems
				return this;
			}
			else if (mw.DeclaringType.ImplementsInterface(ifmethod.DeclaringType))
			{
				return Create(DeclaringType, mw);
			}
			else if (!ifmethod.IsAbstract && !mw.IsAbstract)
			{
				return AddConflictError(mw);
			}
			else if (!ifmethod.IsAbstract && mw.IsAbstract)
			{
				return new ErrorMirandaMethodWrapper(DeclaringType, mw, DeclaringType.Name + "." + Name + Signature);
			}
			else
			{
				return this;
			}
		}

		protected virtual MirandaMethodWrapper AddConflictError(MethodWrapper mw)
		{
			return new ErrorMirandaMethodWrapper(DeclaringType, ifmethod, "Conflicting default methods:")
				.AddConflictError(ifmethod)
				.AddConflictError(mw);
		}

		internal bool IsConflictError
		{
			get { return Error != null && Error.StartsWith("Conflicting default methods:"); }
		}

		internal MethodWrapper BaseMethod
		{
			get { return ifmethod; }
		}

		internal virtual string Error
		{
			get { return null; }
		}

#if EMITTERS
		protected override void CallImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Call, GetMethod());
		}

		protected override void CallvirtImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Callvirt, GetMethod());
		}
#endif // EMITTERS
	}

	sealed class GhostMethodWrapper : SmartMethodWrapper
	{
		private MethodInfo ghostMethod;
		private MethodInfo defaultImpl;

		internal GhostMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, MethodInfo ghostMethod, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags)
			: base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags)
		{
			// make sure we weren't handed the ghostMethod in the wrapper value type
			Debug.Assert(method == null || method.DeclaringType.IsInterface);
			this.ghostMethod = ghostMethod;
		}

#if EMITTERS
		protected override void CallImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Call, defaultImpl);
		}

		protected override void CallvirtImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Call, ghostMethod);
		}
#endif

#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS
		[HideFromJava]
		internal override object Invoke(object obj, object[] args)
		{
			return InvokeAndUnwrapException(ghostMethod, DeclaringType.GhostWrap(obj), args);
		}
#endif

		internal void SetDefaultImpl(MethodInfo impl)
		{
			this.defaultImpl = impl;
		}

		internal MethodInfo GetDefaultImpl()
		{
			return defaultImpl;
		}

#if STATIC_COMPILER
		internal void SetGhostMethod(MethodBuilder mb)
		{
			this.ghostMethod = mb;
		}

		internal MethodBuilder GetGhostMethod()
		{
			return (MethodBuilder)ghostMethod;
		}
#endif
	}

	sealed class AccessStubMethodWrapper : SmartMethodWrapper
	{
		private readonly MethodInfo stubVirtual;
		private readonly MethodInfo stubNonVirtual;

		internal AccessStubMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodInfo core, MethodInfo stubVirtual, MethodInfo stubNonVirtual, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags)
			: base(declaringType, name, sig, core, returnType, parameterTypes, modifiers, flags)
		{
			this.stubVirtual = stubVirtual;
			this.stubNonVirtual = stubNonVirtual;
		}

#if EMITTERS
		protected override void CallImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Call, stubNonVirtual);
		}

		protected override void CallvirtImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Callvirt, stubVirtual);
		}
#endif // EMITTERS
	}

	sealed class AccessStubConstructorMethodWrapper : SmartMethodWrapper
	{
		private readonly ConstructorInfo stub;

		internal AccessStubConstructorMethodWrapper(TypeWrapper declaringType, string sig, ConstructorInfo core, ConstructorInfo stub, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags)
			: base(declaringType, StringConstants.INIT, sig, core, PrimitiveTypeWrapper.VOID, parameterTypes, modifiers, flags)
		{
			this.stub = stub;
		}

#if EMITTERS
		protected override void CallImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Call, stub);
		}

		protected override void NewobjImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Newobj, stub);
		}
#endif // EMITTERS
	}

	sealed class DefaultInterfaceMethodWrapper : SmartMethodWrapper
	{
		private MethodInfo impl;

		internal DefaultInterfaceMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodInfo ifmethod, MethodInfo impl, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags)
			: base(declaringType, name, sig, ifmethod, returnType, parameterTypes, modifiers, flags)
		{
			this.impl = impl;
		}

		internal static MethodInfo GetImpl(MethodWrapper mw)
		{
			DefaultInterfaceMethodWrapper dimw = mw as DefaultInterfaceMethodWrapper;
			if (dimw != null)
			{
				return dimw.impl;
			}
			else
			{
				return ((GhostMethodWrapper)mw).GetDefaultImpl();
			}
		}

		internal static void SetImpl(MethodWrapper mw, MethodInfo impl)
		{
			DefaultInterfaceMethodWrapper dimw = mw as DefaultInterfaceMethodWrapper;
			if (dimw != null)
			{
				dimw.impl = impl;
			}
			else
			{
				((GhostMethodWrapper)mw).SetDefaultImpl(impl);
			}
		}

#if EMITTERS
		protected override void CallImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Call, impl);
		}

		protected override void CallvirtImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Callvirt, GetMethod());
		}
#endif // EMITTERS
	}

	sealed class PrivateInterfaceMethodWrapper : SmartMethodWrapper
	{
		internal PrivateInterfaceMethodWrapper(TypeWrapper declaringType, string name, string sig, MethodBase method, TypeWrapper returnType, TypeWrapper[] parameterTypes, Modifiers modifiers, MemberFlags flags)
			: base(declaringType, name, sig, method, returnType, parameterTypes, modifiers, flags)
		{
		}

#if EMITTERS
		protected override void CallImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Call, GetMethod());
		}

		protected override void CallvirtImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Call, GetMethod());
		}
#endif // EMITTERS
	}

	abstract class FieldWrapper : MemberWrapper
	{
#if !STATIC_COMPILER && !FIRST_PASS && !STUB_GENERATOR
		private volatile java.lang.reflect.Field reflectionField;
		private sun.reflect.FieldAccessor jniAccessor;
#endif
		internal static readonly FieldWrapper[] EmptyArray  = new FieldWrapper[0];
		private FieldInfo field;
		private TypeWrapper fieldType;

		internal FieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, Modifiers modifiers, FieldInfo field, MemberFlags flags)
			: base(declaringType, name, sig, modifiers, flags)
		{
			Debug.Assert(name != null);
			Debug.Assert(sig != null);
			this.fieldType = fieldType;
			this.field = field;
			UpdateNonPublicTypeInSignatureFlag();
#if STATIC_COMPILER
			if (IsFinal
				&& DeclaringType.IsPublic
				&& !DeclaringType.IsInterface
				&& (IsPublic || (IsProtected && !DeclaringType.IsFinal))
				&& !DeclaringType.GetClassLoader().StrictFinalFieldSemantics
				&& DeclaringType.IsDynamic
				&& !(this is ConstantFieldWrapper)
				&& !(this is DynamicPropertyFieldWrapper))
			{
				SetType2FinalField();
			}
#endif
		}

		internal FieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, ExModifiers modifiers, FieldInfo field)
			: this(declaringType, fieldType, name, sig, modifiers.Modifiers, field,
					(modifiers.IsInternal ? MemberFlags.InternalAccess : MemberFlags.None))
		{
		}

		private void UpdateNonPublicTypeInSignatureFlag()
		{
			if ((IsPublic || IsProtected) && fieldType != null && !IsAccessStub)
			{
				if (!fieldType.IsPublic && !fieldType.IsUnloadable)
				{
					SetNonPublicTypeInSignatureFlag();
				}
			}
		}

		internal FieldInfo GetField()
		{
			AssertLinked();
			return field;
		}

		[Conditional("DEBUG")]
		internal void AssertLinked()
		{
			if(fieldType == null)
			{
				Tracer.Error(Tracer.Runtime, "AssertLinked failed: " + this.DeclaringType.Name + "::" + this.Name + " (" + this.Signature + ")");
			}
			Debug.Assert(fieldType != null, this.DeclaringType.Name + "::" + this.Name + " (" + this.Signature+ ")");
		}

#if !STATIC_COMPILER && !STUB_GENERATOR
		internal static FieldWrapper FromField(java.lang.reflect.Field field)
		{
#if FIRST_PASS
			return null;
#else
			int slot = field._slot();
			if (slot == -1)
			{
				// it's a Field created by Unsafe.objectFieldOffset(Class,String) so we must resolve based on the name
				foreach (FieldWrapper fw in TypeWrapper.FromClass(field.getDeclaringClass()).GetFields())
				{
					if (fw.Name == field.getName())
					{
						return fw;
					}
				}
			}
			return TypeWrapper.FromClass(field.getDeclaringClass()).GetFields()[slot];
#endif
		}

		internal object ToField(bool copy)
		{
			return ToField(copy, null);
		}

		internal object ToField(bool copy, int? fieldIndex)
		{
#if FIRST_PASS
			return null;
#else
			java.lang.reflect.Field field = reflectionField;
			if (field == null)
			{
				const Modifiers ReflectionFieldModifiersMask = Modifiers.Public | Modifiers.Private | Modifiers.Protected | Modifiers.Static
					| Modifiers.Final | Modifiers.Volatile | Modifiers.Transient | Modifiers.Synthetic | Modifiers.Enum;
				Link();
				field = new java.lang.reflect.Field(
					this.DeclaringType.ClassObject,
					this.Name,
					this.FieldTypeWrapper.EnsureLoadable(this.DeclaringType.GetClassLoader()).ClassObject,
					(int)(this.Modifiers & ReflectionFieldModifiersMask) | (this.IsInternal ? 0x40000000 : 0),
					fieldIndex ?? Array.IndexOf(this.DeclaringType.GetFields(), this),
					this.DeclaringType.GetGenericFieldSignature(this),
					null
				);
			}
			lock (this)
			{
				if (reflectionField == null)
				{
					reflectionField = field;
				}
				else
				{
					field = reflectionField;
				}
			}
			if (copy)
			{
				field = field.copy();
			}
			return field;
#endif // FIRST_PASS
		}
#endif // !STATIC_COMPILER && !STUB_GENERATOR

		[System.Security.SecurityCritical]
		internal static FieldWrapper FromCookie(IntPtr cookie)
		{
			return (FieldWrapper)FromCookieImpl(cookie);
		}

		internal TypeWrapper FieldTypeWrapper
		{
			get
			{
				AssertLinked();
				return fieldType;
			}
		}

#if EMITTERS
		internal void EmitGet(CodeEmitter ilgen)
		{
			AssertLinked();
			EmitGetImpl(ilgen);
		}

		protected abstract void EmitGetImpl(CodeEmitter ilgen);

		internal void EmitSet(CodeEmitter ilgen)
		{
			AssertLinked();
			EmitSetImpl(ilgen);
		}

		protected abstract void EmitSetImpl(CodeEmitter ilgen);
#endif // EMITTERS


#if STATIC_COMPILER
		internal bool IsLinked
		{
			get { return fieldType != null; }
		}
#endif

		internal void Link()
		{
			lock(this)
			{
				if(fieldType != null)
				{
					return;
				}
			}
			TypeWrapper fld = this.DeclaringType.GetClassLoader().FieldTypeWrapperFromSigNoThrow(Signature);
			lock(this)
			{
				try
				{
					// critical code in the finally block to avoid Thread.Abort interrupting the thread
				}
				finally
				{
					if(fieldType == null)
					{
						fieldType = fld;
						UpdateNonPublicTypeInSignatureFlag();
						try
						{
							field = this.DeclaringType.LinkField(this);
						}
						catch
						{
							// HACK if linking fails, we unlink to make sure
							// that the next link attempt will fail again
							fieldType = null;
							throw;
						}
					}
				}
			}
		}

		internal bool IsVolatile
		{
			get
			{
				return (Modifiers & Modifiers.Volatile) != 0;
			}
		}

		internal static FieldWrapper Create(TypeWrapper declaringType, TypeWrapper fieldType, FieldInfo fi, string name, string sig, ExModifiers modifiers)
		{
			// volatile long & double field accesses must be made atomic
			if((modifiers.Modifiers & Modifiers.Volatile) != 0 && (sig == "J" || sig == "D"))
			{
				return new VolatileLongDoubleFieldWrapper(declaringType, fieldType, fi, name, sig, modifiers);
			}
			return new SimpleFieldWrapper(declaringType, fieldType, fi, name, sig, modifiers);
		}

#if !STATIC_COMPILER && !STUB_GENERATOR
		internal virtual void ResolveField()
		{
			FieldBuilder fb = field as FieldBuilder;
			if(fb != null)
			{
				field = fb.Module.ResolveField(fb.GetToken().Token);
			}
		}

		internal object GetFieldAccessorJNI()
		{
#if FIRST_PASS
			return null;
#else
			if (jniAccessor == null)
			{
				Interlocked.CompareExchange(ref jniAccessor, Java_sun_reflect_ReflectionFactory.NewFieldAccessorJNI(this), null);
			}
			return jniAccessor;
#endif
		}

#if !FIRST_PASS
		internal abstract object GetValue(object obj);
		internal abstract void SetValue(object obj, object value);
#endif
#endif // !STATIC_COMPILER && !STUB_GENERATOR
	}

	sealed class SimpleFieldWrapper : FieldWrapper
	{
		internal SimpleFieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, FieldInfo fi, string name, string sig, ExModifiers modifiers)
			: base(declaringType, fieldType, name, sig, modifiers, fi)
		{
			Debug.Assert(!(fieldType == PrimitiveTypeWrapper.DOUBLE || fieldType == PrimitiveTypeWrapper.LONG) || !IsVolatile);
		}

#if EMITTERS
		protected override void EmitGetImpl(CodeEmitter ilgen)
		{
			if(!IsStatic && DeclaringType.IsNonPrimitiveValueType)
			{
				ilgen.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD);
			}
			if(IsVolatile)
			{
				ilgen.Emit(OpCodes.Volatile);
			}
			ilgen.Emit(IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, GetField());
		}

		protected override void EmitSetImpl(CodeEmitter ilgen)
		{
			FieldInfo fi = GetField();
			if(!IsStatic && DeclaringType.IsNonPrimitiveValueType)
			{
				CodeEmitterLocal temp = ilgen.DeclareLocal(FieldTypeWrapper.TypeAsSignatureType);
				ilgen.Emit(OpCodes.Stloc, temp);
				ilgen.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD);
				ilgen.Emit(OpCodes.Ldloc, temp);
			}
			if(IsVolatile)
			{
				ilgen.Emit(OpCodes.Volatile);
			}
			ilgen.Emit(IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fi);
			if(IsVolatile)
			{
				ilgen.EmitMemoryBarrier();
			}
		}
#endif // EMITTERS

#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS
		internal override object GetValue(object obj)
		{
			return GetField().GetValue(obj);
		}

		internal override void SetValue(object obj, object value)
		{
			GetField().SetValue(obj, value);
		}
#endif // !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS
	}

	sealed class VolatileLongDoubleFieldWrapper : FieldWrapper
	{
		internal VolatileLongDoubleFieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, FieldInfo fi, string name, string sig, ExModifiers modifiers)
			: base(declaringType, fieldType, name, sig, modifiers, fi)
		{
			Debug.Assert(IsVolatile);
			Debug.Assert(sig == "J" || sig == "D");
		}

#if EMITTERS
		protected override void EmitGetImpl(CodeEmitter ilgen)
		{
			FieldInfo fi = GetField();
			if(fi.IsStatic)
			{
				ilgen.Emit(OpCodes.Ldsflda, fi);
			}
			else
			{
				if(DeclaringType.IsNonPrimitiveValueType)
				{
					ilgen.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD);
				}
				ilgen.Emit(OpCodes.Ldflda, fi);
			}
			if(FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE)
			{
				ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.volatileReadDouble);
			}
			else
			{
				Debug.Assert(FieldTypeWrapper == PrimitiveTypeWrapper.LONG);
				ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.volatileReadLong);
			}
		}

		protected override void EmitSetImpl(CodeEmitter ilgen)
		{
			FieldInfo fi = GetField();
			CodeEmitterLocal temp = ilgen.DeclareLocal(FieldTypeWrapper.TypeAsSignatureType);
			ilgen.Emit(OpCodes.Stloc, temp);
			if(fi.IsStatic)
			{
				ilgen.Emit(OpCodes.Ldsflda, fi);
			}
			else
			{
				if(DeclaringType.IsNonPrimitiveValueType)
				{
					ilgen.Emit(OpCodes.Unbox, DeclaringType.TypeAsTBD);
				}
				ilgen.Emit(OpCodes.Ldflda, fi);
			}
			ilgen.Emit(OpCodes.Ldloc, temp);
			if(FieldTypeWrapper == PrimitiveTypeWrapper.DOUBLE)
			{
				ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.volatileWriteDouble);
			}
			else
			{
				Debug.Assert(FieldTypeWrapper == PrimitiveTypeWrapper.LONG);
				ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.volatileWriteLong);
			}
		}
#endif // EMITTERS

#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS
#if NO_REF_EMIT
		internal static readonly object lockObject = new object();
#endif

		internal override object GetValue(object obj)
		{
#if NO_REF_EMIT
			lock (lockObject)
			{
				return GetField().GetValue(obj);
			}
#else
			throw new InvalidOperationException();
#endif
		}

		internal override void SetValue(object obj, object value)
		{
#if NO_REF_EMIT
			lock (lockObject)
			{
				GetField().SetValue(obj, value);
			}
#else
			throw new InvalidOperationException();
#endif
		}
#endif // !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS
	}

#if !STUB_GENERATOR
	// this class represents a .NET property defined in Java with the ikvm.lang.Property annotation
	sealed class DynamicPropertyFieldWrapper : FieldWrapper
	{
		private readonly MethodWrapper getter;
		private readonly MethodWrapper setter;
		private PropertyBuilder pb;

		private MethodWrapper GetMethod(string name, string sig, bool isstatic)
		{
			if(name != null)
			{
				MethodWrapper mw = this.DeclaringType.GetMethodWrapper(name, sig, false);
				if(mw != null && mw.IsStatic == isstatic)
				{
					mw.IsPropertyAccessor = true;
					return mw;
				}
				Tracer.Error(Tracer.Compiler, "Property '{0}' accessor '{1}' not found in class '{2}'", this.Name, name, this.DeclaringType.Name);
			}
			return null;
		}

		internal DynamicPropertyFieldWrapper(TypeWrapper declaringType, ClassFile.Field fld)
			: base(declaringType, null, fld.Name, fld.Signature, new ExModifiers(fld.Modifiers, fld.IsInternal), null)
		{
			getter = GetMethod(fld.PropertyGetter, "()" + fld.Signature, fld.IsStatic);
			setter = GetMethod(fld.PropertySetter, "(" + fld.Signature + ")V", fld.IsStatic);
		}

#if !STATIC_COMPILER && !FIRST_PASS
		internal override void ResolveField()
		{
			if (getter != null)
			{
				getter.ResolveMethod();
			}
			if (setter != null)
			{
				setter.ResolveMethod();
			}
		}
#endif

		internal PropertyBuilder GetPropertyBuilder()
		{
			AssertLinked();
			return pb;
		}

		internal void DoLink(TypeBuilder tb)
		{
			if(getter != null)
			{
				getter.Link();
			}
			if(setter != null)
			{
				setter.Link();
			}
			pb = tb.DefineProperty(this.Name, PropertyAttributes.None, this.FieldTypeWrapper.TypeAsSignatureType, Type.EmptyTypes);
			if(getter != null)
			{
				pb.SetGetMethod((MethodBuilder)getter.GetMethod());
			}
			if(setter != null)
			{
				pb.SetSetMethod((MethodBuilder)setter.GetMethod());
			}
#if STATIC_COMPILER
			AttributeHelper.SetModifiers(pb, this.Modifiers, this.IsInternal);
#endif
		}

#if EMITTERS
		protected override void EmitGetImpl(CodeEmitter ilgen)
		{
			if(getter == null)
			{
				EmitThrowNoSuchMethodErrorForGetter(ilgen, this.FieldTypeWrapper, this.IsStatic);
			}
			else if(getter.IsStatic)
			{
				getter.EmitCall(ilgen);
			}
			else
			{
				getter.EmitCallvirt(ilgen);
			}
		}

		internal static void EmitThrowNoSuchMethodErrorForGetter(CodeEmitter ilgen, TypeWrapper type, bool isStatic)
		{
			// HACK the branch around the throw is to keep the verifier happy
			CodeEmitterLabel label = ilgen.DefineLabel();
			ilgen.Emit(OpCodes.Ldc_I4_0);
			ilgen.EmitBrtrue(label);
			ilgen.EmitThrow("java.lang.NoSuchMethodError");
			ilgen.MarkLabel(label);
			if (!isStatic)
			{
				ilgen.Emit(OpCodes.Pop);
			}
			ilgen.Emit(OpCodes.Ldloc, ilgen.DeclareLocal(type.TypeAsLocalOrStackType));
		}

		protected override void EmitSetImpl(CodeEmitter ilgen)
		{
			if(setter == null)
			{
				if(this.IsFinal)
				{
					ilgen.Emit(OpCodes.Pop);
					if(!this.IsStatic)
					{
						ilgen.Emit(OpCodes.Pop);
					}
				}
				else
				{
					EmitThrowNoSuchMethodErrorForSetter(ilgen, this.IsStatic);
				}
			}
			else if(setter.IsStatic)
			{
				setter.EmitCall(ilgen);
			}
			else
			{
				setter.EmitCallvirt(ilgen);
			}
		}

		internal static void EmitThrowNoSuchMethodErrorForSetter(CodeEmitter ilgen, bool isStatic)
		{
			// HACK the branch around the throw is to keep the verifier happy
			CodeEmitterLabel label = ilgen.DefineLabel();
			ilgen.Emit(OpCodes.Ldc_I4_0);
			ilgen.EmitBrtrue(label);
			ilgen.EmitThrow("java.lang.NoSuchMethodError");
			ilgen.MarkLabel(label);
			ilgen.Emit(OpCodes.Pop);
			if (!isStatic)
			{
				ilgen.Emit(OpCodes.Pop);
			}
		}
#endif // EMITTERS

#if !STATIC_COMPILER && !FIRST_PASS
		internal override object GetValue(object obj)
		{
			if (getter == null)
			{
				throw new java.lang.NoSuchMethodError();
			}
			return getter.Invoke(obj, new object[0]);
		}

		internal override void SetValue(object obj, object value)
		{
			if (setter == null)
			{
				throw new java.lang.NoSuchMethodError();
			}
			setter.Invoke(obj, new object[] { value });
		}
#endif
	}
#endif // !STUB_GENERATOR

	// this class represents a .NET property defined in Java with the ikvm.lang.Property annotation
	sealed class CompiledPropertyFieldWrapper : FieldWrapper
	{
		private readonly PropertyInfo property;

		internal CompiledPropertyFieldWrapper(TypeWrapper declaringType, PropertyInfo property, ExModifiers modifiers)
			: base(declaringType, ClassLoaderWrapper.GetWrapperFromType(property.PropertyType), property.Name, ClassLoaderWrapper.GetWrapperFromType(property.PropertyType).SigName, modifiers, null)
		{
			this.property = property;
		}

#if EMITTERS
		protected override void EmitGetImpl(CodeEmitter ilgen)
		{
			MethodInfo getter = property.GetGetMethod(true);
			if(getter == null)
			{
				DynamicPropertyFieldWrapper.EmitThrowNoSuchMethodErrorForGetter(ilgen, this.FieldTypeWrapper, this.IsStatic);
			}
			else if(getter.IsStatic)
			{
				ilgen.Emit(OpCodes.Call, getter);
			}
			else
			{
				ilgen.Emit(OpCodes.Callvirt, getter);
			}
		}

		protected override void EmitSetImpl(CodeEmitter ilgen)
		{
			MethodInfo setter = property.GetSetMethod(true);
			if (setter == null)
			{
				if(this.IsFinal)
				{
					ilgen.Emit(OpCodes.Pop);
					if(!this.IsStatic)
					{
						ilgen.Emit(OpCodes.Pop);
					}
				}
				else
				{
					DynamicPropertyFieldWrapper.EmitThrowNoSuchMethodErrorForSetter(ilgen, this.IsStatic);
				}
			}
			else if(setter.IsStatic)
			{
				ilgen.Emit(OpCodes.Call, setter);
			}
			else
			{
				ilgen.Emit(OpCodes.Callvirt, setter);
			}
		}
#endif // EMITTERS

#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS
		internal override object GetValue(object obj)
		{
			MethodInfo getter = property.GetGetMethod(true);
			if (getter == null)
			{
				throw new java.lang.NoSuchMethodError();
			}
			return getter.Invoke(obj, new object[0]);
		}

		internal override void SetValue(object obj, object value)
		{
			MethodInfo setter = property.GetSetMethod(true);
			if (setter == null)
			{
				throw new java.lang.NoSuchMethodError();
			}
			setter.Invoke(obj, new object[] { value });
		}
#endif

		internal PropertyInfo GetProperty()
		{
			return property;
		}
	}

	sealed class ConstantFieldWrapper : FieldWrapper
	{
		// NOTE this field wrapper can resprent a .NET enum, but in that case "constant" contains the raw constant value (i.e. the boxed underlying primitive value, not a boxed enum)
		private object constant;

		internal ConstantFieldWrapper(TypeWrapper declaringType, TypeWrapper fieldType, string name, string sig, Modifiers modifiers, FieldInfo field, object constant, MemberFlags flags)
			: base(declaringType, fieldType, name, sig, modifiers, field, flags)
		{
			Debug.Assert(IsStatic);
            Debug.Assert(constant == null || constant.GetType().IsPrimitive || constant is string);
			this.constant = constant;
		}

#if EMITTERS
		protected override void EmitGetImpl(CodeEmitter ilgen)
		{
			// Reading a field should trigger the cctor, but since we're inlining the value
			// we have to trigger it explicitly
			DeclaringType.EmitRunClassConstructor(ilgen);

			// NOTE even though you're not supposed to access a constant static final (the compiler is supposed
			// to inline them), we have to support it (because it does happen, e.g. if the field becomes final
			// after the referencing class was compiled, or when we're accessing an unsigned primitive .NET field)
			object v = GetConstantValue();
			if(v == null)
			{
				ilgen.Emit(OpCodes.Ldnull);
			}
			else if(constant is int || 
				constant is short ||
				constant is ushort ||
				constant is byte ||
				constant is sbyte ||
				constant is char ||
				constant is bool)
			{
				ilgen.EmitLdc_I4(((IConvertible)constant).ToInt32(null));
			}
			else if(constant is string)
			{
				ilgen.Emit(OpCodes.Ldstr, (string)constant);
			}
			else if(constant is float)
			{
				ilgen.EmitLdc_R4((float)constant);
			}
			else if(constant is double)
			{
				ilgen.EmitLdc_R8((double)constant);
			}
			else if(constant is long)
			{
				ilgen.EmitLdc_I8((long)constant);
			}
			else if(constant is uint)
			{
				ilgen.EmitLdc_I4(unchecked((int)((IConvertible)constant).ToUInt32(null)));
			}
			else if(constant is ulong)
			{
				ilgen.EmitLdc_I8(unchecked((long)(ulong)constant));
			}
			else
			{
				throw new InvalidOperationException(constant.GetType().FullName);
			}
		}

		protected override void EmitSetImpl(CodeEmitter ilgen)
		{
			// when constant static final fields are updated, the JIT normally doesn't see that (because the
			// constant value is inlined), so we emulate that behavior by emitting a Pop
			ilgen.Emit(OpCodes.Pop);
		}
#endif // EMITTERS

		internal object GetConstantValue()
		{
			if(constant == null)
			{
				FieldInfo field = GetField();
				constant = field.GetRawConstantValue();
			}
			return constant;
		}

#if !STUB_GENERATOR && !STATIC_COMPILER && !FIRST_PASS
		internal override object GetValue(object obj)
		{
			FieldInfo field = GetField();
			return FieldTypeWrapper.IsPrimitive || field == null
				? GetConstantValue()
				: field.GetValue(null);
		}

		internal override void SetValue(object obj, object value)
		{
		}
#endif
	}

	sealed class CompiledAccessStubFieldWrapper : FieldWrapper
	{
		private readonly MethodInfo getter;
		private readonly MethodInfo setter;

		private static Modifiers GetModifiers(PropertyInfo property)
		{
			// NOTE we only support the subset of modifiers that is expected for "access stub" properties
			MethodInfo getter = property.GetGetMethod(true);
			Modifiers modifiers = getter.IsPublic ? Modifiers.Public : Modifiers.Protected;
			if(!property.CanWrite)
			{
				modifiers |= Modifiers.Final;
			}
			if(getter.IsStatic)
			{
				modifiers |= Modifiers.Static;
			}
			return modifiers;
		}

		// constructor for type 1 access stubs
		internal CompiledAccessStubFieldWrapper(TypeWrapper wrapper, PropertyInfo property, TypeWrapper propertyType)
			: this(wrapper, property, null, propertyType, GetModifiers(property), MemberFlags.HideFromReflection | MemberFlags.AccessStub)
		{
		}

		// constructor for type 2 access stubs
		internal CompiledAccessStubFieldWrapper(TypeWrapper wrapper, PropertyInfo property, FieldInfo field, TypeWrapper propertyType)
			: this(wrapper, property, field, propertyType, AttributeHelper.GetModifiersAttribute(property).Modifiers, MemberFlags.AccessStub)
		{
		}

		private CompiledAccessStubFieldWrapper(TypeWrapper wrapper, PropertyInfo property, FieldInfo field, TypeWrapper propertyType, Modifiers modifiers, MemberFlags flags)
			: base(wrapper, propertyType, property.Name, propertyType.SigName, modifiers, field, flags)
		{
			this.getter = property.GetGetMethod(true);
			this.setter = property.GetSetMethod(true);
		}

#if EMITTERS
		protected override void EmitGetImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Call, getter);
		}

		protected override void EmitSetImpl(CodeEmitter ilgen)
		{
			ilgen.Emit(OpCodes.Call, setter);
		}
#endif // EMITTERS

#if !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS
		internal override object GetValue(object obj)
		{
			// we can only be invoked on type 2 access stubs (because type 1 access stubs are HideFromReflection), so we know we have a field
			return GetField().GetValue(obj);
		}

		internal override void SetValue(object obj, object value)
		{
			// we can only be invoked on type 2 access stubs (because type 1 access stubs are HideFromReflection), so we know we have a field
			GetField().SetValue(obj, value);
		}
#endif // !STATIC_COMPILER && !STUB_GENERATOR && !FIRST_PASS
	}
}
