context: /*
    Copyright (C) 2002-2008 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.Reflection;
  using System.Diagnostics;
  using System.Threading;
  using IKVM.Attributes;
  using IKVM.Internal;
  using System.Runtime.InteropServices;
  
  namespace IKVM.Runtime
  {
  	static class GhostTag
  	{
  		private static volatile PassiveWeakDictionary<object, TypeWrapper> dict;
  
  		internal static void SetTag(object obj, RuntimeTypeHandle typeHandle)
  		{
  			SetTag(obj, ClassLoaderWrapper.GetWrapperFromType(Type.GetTypeFromHandle(typeHandle)));
  		}
  
  		internal static void SetTag(object obj, TypeWrapper wrapper)
  		{
  			if(dict == null)
  			{
  				PassiveWeakDictionary<object, TypeWrapper> newDict = new PassiveWeakDictionary<object, TypeWrapper>();
  #pragma warning disable 0420 // don't whine about CompareExchange not respecting 'volatile'
  				if(Interlocked.CompareExchange(ref dict, newDict, null) != null)
  #pragma warning restore
  				{
  					newDict.Dispose();
  				}
  			}
  			dict.Add(obj, wrapper);
  		}
  
  		internal static TypeWrapper GetTag(object obj)
  		{
  			if(dict != null)
  			{
  				TypeWrapper tw;
  				dict.TryGetValue(obj, out tw);
  				return tw;
  			}
  			return null;
  		}
  
  		// this method is called from <GhostType>.IsInstanceArray()
  		internal static bool IsGhostArrayInstance(object obj, RuntimeTypeHandle typeHandle, int rank)
  		{
  			TypeWrapper tw1 = GhostTag.GetTag(obj);
  			if(tw1 != null)
  			{
  				TypeWrapper tw2 = ClassLoaderWrapper.GetWrapperFromType(Type.GetTypeFromHandle(typeHandle)).MakeArrayType(rank);
  				return tw1.IsAssignableTo(tw2);
  			}
  			return false;
  		}
  
  		// this method is called from <GhostType>.CastArray()
  		[HideFromJava]
  		internal static void ThrowClassCastException(object obj, RuntimeTypeHandle typeHandle, int rank)
  		{
  #if !FIRST_PASS
  			System.Text.StringBuilder sb = new System.Text.StringBuilder();
  			sb.Append(ikvm.runtime.Util.getClassFromObject(obj).getName()).Append(" cannot be cast to ")
  				.Append('[', rank).Append('L').Append(ikvm.runtime.Util.getClassFromTypeHandle(typeHandle).getName()).Append(';');
  			throw new java.lang.ClassCastException(sb.ToString());
  #endif
  		}
  	}
  
  	public static class ByteCodeHelper
  	{
  		[DebuggerStepThroughAttribute]
  		public static object multianewarray(RuntimeTypeHandle typeHandle, int[] lengths)
  		{
  			for(int i = 0; i < lengths.Length; i++)
  			{
  				if(lengths[i] < 0)
  				{
  #if !FIRST_PASS
  					throw new java.lang.NegativeArraySizeException();
  #endif
  				}
  			}
  			return MultianewarrayHelper(Type.GetTypeFromHandle(typeHandle).GetElementType(), lengths, 0);
  		}
  
  		private static object MultianewarrayHelper(Type elemType, int[] lengths, int index)
  		{
  			object o = Array.CreateInstance(elemType, lengths[index++]);
  			if(index < lengths.Length)
  			{
  				elemType = elemType.GetElementType();
  				object[] a = (object[])o;
  				for(int i = 0; i < a.Length; i++)
  				{
  					a[i] = MultianewarrayHelper(elemType, lengths, index);
  				}
  			}
  			return o;
  		}
  
  		[DebuggerStepThroughAttribute]
  		public static object multianewarray_ghost(RuntimeTypeHandle typeHandle, int[] lengths)
  		{
  			Type type = Type.GetTypeFromHandle(typeHandle);
  			int rank = 0;
  			while(ReflectUtil.IsVector(type))
  			{
  				rank++;
  				type = type.GetElementType();
  			}
  			object obj = multianewarray(ArrayTypeWrapper.MakeArrayType(typeof(object), rank).TypeHandle, lengths);
  			GhostTag.SetTag(obj, typeHandle);
  			return obj;
  		}
  
  		[DebuggerStepThroughAttribute]
  		public static T[] anewarray_ghost<T>(int length, RuntimeTypeHandle typeHandle)
  		{
  			T[] obj = new T[length];
  			GhostTag.SetTag(obj, typeHandle);
  			return obj;
  		}
  
  #if !FIRST_PASS
  		[DebuggerStepThroughAttribute]
  		public static object DynamicMultianewarray(RuntimeTypeHandle type, string clazz, int[] lengths)
  		{
  			Profiler.Count("DynamicMultianewarray");
  			TypeWrapper wrapper = LoadTypeWrapper(type, clazz);
  			return multianewarray(wrapper.TypeAsArrayType.TypeHandle, lengths);
  		}
  
  		[DebuggerStepThroughAttribute]
  		public static object DynamicNewarray(int length, RuntimeTypeHandle type, string clazz)
  		{
  			Profiler.Count("DynamicNewarray");
  			if(length < 0)
  			{
  				throw new java.lang.NegativeArraySizeException();
  			}
  			TypeWrapper wrapper = LoadTypeWrapper(type, clazz);
  			return Array.CreateInstance(wrapper.TypeAsArrayType, length);
  		}
  
  		[DebuggerStepThroughAttribute]
  		public static void DynamicAastore(object arrayref, int index, object val, RuntimeTypeHandle type, string clazz)
  		{
  			Profiler.Count("DynamicAastore");
  			// TODO do we need to load the type here?
  			((Array)arrayref).SetValue(val, index);
  		}
  
  		[DebuggerStepThroughAttribute]
  		public static object DynamicAaload(object arrayref, int index, RuntimeTypeHandle type, string clazz)
  		{
  			Profiler.Count("DynamicAaload");
  			// TODO do we need to load the type here?
  			return ((Array)arrayref).GetValue(index);
  		}
  
  		private static FieldWrapper GetFieldWrapper(object thisObj, RuntimeTypeHandle type, string clazz, string name, string sig, bool isStatic)
  		{
  			TypeWrapper caller = ClassLoaderWrapper.GetWrapperFromType(Type.GetTypeFromHandle(type));
  			TypeWrapper wrapper = LoadTypeWrapper(type, clazz);
  			FieldWrapper field = wrapper.GetFieldWrapper(name, sig);
  			if(field == null)
  			{
  				throw new java.lang.NoSuchFieldError(clazz + "." + name);
  			}
  			// TODO check loader constraints
  			if(field.IsStatic != isStatic)
  			{
  				throw new java.lang.IncompatibleClassChangeError(clazz + "." + name);
  			}
  			TypeWrapper objType = null;
  			if(thisObj != null)
  			{
  				objType = ClassLoaderWrapper.GetWrapperFromType(thisObj.GetType());
  			}
  			if(field.IsAccessibleFrom(wrapper, caller, objType))
  			{
  				return field;
  			}
  			throw new java.lang.IllegalAccessError(field.DeclaringType.Name + "." + name);
  		}
  
  		[DebuggerStepThroughAttribute]
  		public static object DynamicGetfield(object obj, string name, string sig, RuntimeTypeHandle type, string clazz, ikvm.@internal.CallerID callerID)
  		{
  			Profiler.Count("DynamicGetfield");
  			FieldWrapper fw = GetFieldWrapper(obj, type, clazz, name, sig, false);
  			java.lang.reflect.Field field = (java.lang.reflect.Field)fw.ToField(false);
  			object val = field.get(obj, callerID);
  			if(fw.FieldTypeWrapper.IsPrimitive)
  			{
  				val = JVM.Unbox(val);
  			}
  			return val;
  		}
  
  		[DebuggerStepThroughAttribute]
  		public static object DynamicGetstatic(string name, string sig, RuntimeTypeHandle type, string clazz, ikvm.@internal.CallerID callerID)
  		{
  			Profiler.Count("DynamicGetstatic");
  			FieldWrapper fw = GetFieldWrapper(null, type, clazz, name, sig, true);
  			java.lang.reflect.Field field = (java.lang.reflect.Field)fw.ToField(false);
  			object val = field.get(null, callerID);
  			if(fw.FieldTypeWrapper.IsPrimitive)
  			{
  				val = JVM.Unbox(val);
  			}
  			return val;
  		}
  
  		[DebuggerStepThroughAttribute]
  		public static void DynamicPutfield(object obj, object val, string name, string sig, RuntimeTypeHandle type, string clazz, ikvm.@internal.CallerID callerID)
  		{
  			Profiler.Count("DynamicPutfield");
  			FieldWrapper fw = GetFieldWrapper(obj, type, clazz, name, sig, false);
  			if(fw.IsFinal)
  			{
  				throw new java.lang.IllegalAccessError("Field " + fw.DeclaringType.Name + "." + fw.Name + " is final");
  			}
  			java.lang.reflect.Field field = (java.lang.reflect.Field)fw.ToField(false);
  			if(fw.FieldTypeWrapper.IsPrimitive)
  			{
  				val = JVM.Box(val);
  			}
  			field.set(obj, val, callerID);
  		}
  
  		[DebuggerStepThroughAttribute]
  		public static void DynamicPutstatic(object val, string name, string sig, RuntimeTypeHandle type, string clazz, ikvm.@internal.CallerID callerID)
  		{
  			Profiler.Count("DynamicPutstatic");
  			FieldWrapper fw = GetFieldWrapper(null, type, clazz, name, sig, true);
  			if(fw.IsFinal)
  			{
  				throw new java.lang.IllegalAccessError("Field " + fw.DeclaringType.Name + "." + fw.Name + " is final");
  			}
  			java.lang.reflect.Field field = (java.lang.reflect.Field)fw.ToField(false);
  			if(fw.FieldTypeWrapper.IsPrimitive)
  			{
  				val = JVM.Box(val);
  			}
  			field.set(null, val, callerID);
  		}
  
  		// the sole purpose of this method is to check whether the clazz can be instantiated (but not to actually do it)
  		[DebuggerStepThroughAttribute]
  		public static void DynamicNewCheckOnly(RuntimeTypeHandle type, string clazz)
  		{
  			Profiler.Count("DynamicNewCheckOnly");
  			TypeWrapper wrapper = LoadTypeWrapper(type, clazz);
  			if(wrapper.IsAbstract)
  			{
  				throw new java.lang.InstantiationError(clazz);
  			}
  			wrapper.RunClassInit();
  		}
  
  		private static TypeWrapper LoadTypeWrapper(RuntimeTypeHandle type, string clazz)
  		{
  			try
  			{
  				TypeWrapper context = ClassLoaderWrapper.GetWrapperFromType(Type.GetTypeFromHandle(type));
  				TypeWrapper wrapper = context.GetClassLoader().LoadClassByDottedNameFast(clazz);
  				if(wrapper == null)
  				{
  					throw new java.lang.NoClassDefFoundError(clazz);
  				}
  				if(!wrapper.IsAccessibleFrom(context))
  				{
  					throw new java.lang.IllegalAccessError("Try to access class " + wrapper.Name + " from class " + context.Name);
  				}
  				wrapper.Finish();
  				return wrapper;
  			}
  			catch(RetargetableJavaException x)
  			{
  				throw x.ToJava();
  			}
  		}
  
  		[DebuggerStepThroughAttribute]
  		public static object DynamicClassLiteral(RuntimeTypeHandle type, string clazz)
  		{
  			Profiler.Count("DynamicClassLiteral");
  			return LoadTypeWrapper(type, clazz).ClassObject;
  		}
  
  		[DebuggerStepThroughAttribute]
  		public static object DynamicCast(object obj, RuntimeTypeHandle type, string clazz)
  		{
  			Profiler.Count("DynamicCast");
  			// NOTE it's important that we don't try to load the class if obj == null
  			// (to be compatible with Sun)
  			if(obj != null && !DynamicInstanceOf(obj, type, clazz))
  			{
  				throw new java.lang.ClassCastException(NativeCode.ikvm.runtime.Util.GetTypeWrapperFromObject(obj).Name);
  			}
  			return obj;
  		}
  
  		[DebuggerStepThroughAttribute]
  		public static bool DynamicInstanceOf(object obj, RuntimeTypeHandle type, string clazz)
  		{
  			Profiler.Count("DynamicInstanceOf");
  			// NOTE it's important that we don't try to load the class if obj == null
  			// (to be compatible with Sun)
  			if(obj == null)
  			{
  				return false;
  			}
  			TypeWrapper wrapper = LoadTypeWrapper(type, clazz);
  			return wrapper.IsInstance(obj);
  		}
  
  		private static MethodWrapper GetMethodWrapper(object thisObj, RuntimeTypeHandle type, string clazz, string name, string sig, bool isStatic)
  		{
  			TypeWrapper caller = ClassLoaderWrapper.GetWrapperFromType(Type.GetTypeFromHandle(type));
  			TypeWrapper wrapper = LoadTypeWrapper(type, clazz);
  			MethodWrapper mw = wrapper.GetMethodWrapper(name, sig, false);
  			if(mw == null)
  			{
  
gt: 				throw new java.lang.NoSuchMethodError(clazz + "." + name + sig);