//
// MonoMethod.cs: The class used to represent methods from the mono runtime.
//
// Authors:
//   Paolo Molaro (lupus@ximian.com)
//   Marek Safar (marek.safar@gmail.com)
//
// (C) 2001 Ximian, Inc.  http://www.ximian.com
// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
#if !FULL_AOT_RUNTIME
using System.Reflection.Emit;
#endif
using System.Security;
using System.Threading;
using System.Text;
using System.Diagnostics;

namespace System.Reflection {
	
	internal struct MonoMethodInfo 
	{
#pragma warning disable 649	
		private Type parent;
		private Type ret;
		internal MethodAttributes attrs;
		internal MethodImplAttributes iattrs;
		private CallingConventions callconv;
#pragma warning restore 649		

		[MethodImplAttribute(MethodImplOptions.InternalCall)]
		static extern void get_method_info (IntPtr handle, out MonoMethodInfo info);
		
		[MethodImplAttribute(MethodImplOptions.InternalCall)]
		static extern int get_method_attributes (IntPtr handle);
		
		internal static MonoMethodInfo GetMethodInfo (IntPtr handle)
		{
			MonoMethodInfo info;
			MonoMethodInfo.get_method_info (handle, out info);
			return info;
		}

		internal static Type GetDeclaringType (IntPtr handle)
		{
			return GetMethodInfo (handle).parent;
		}

		internal static Type GetReturnType (IntPtr handle)
		{
			return GetMethodInfo (handle).ret;
		}

		internal static MethodAttributes GetAttributes (IntPtr handle)
		{
			return (MethodAttributes)get_method_attributes (handle);
		}

		internal static CallingConventions GetCallingConvention (IntPtr handle)
		{
			return GetMethodInfo (handle).callconv;
		}

		internal static MethodImplAttributes GetMethodImplementationFlags (IntPtr handle)
		{
			return GetMethodInfo (handle).iattrs;
		}

		[MethodImplAttribute(MethodImplOptions.InternalCall)]
		static extern ParameterInfo[] get_parameter_info (IntPtr handle, MemberInfo member);

		static internal ParameterInfo[] GetParametersInfo (IntPtr handle, MemberInfo member)
		{
			return get_parameter_info (handle, member);
		}

		[MethodImplAttribute(MethodImplOptions.InternalCall)]
		static extern MarshalAsAttribute get_retval_marshal (IntPtr handle);

		static internal ParameterInfo GetReturnParameterInfo (MonoMethod method)
		{
			return ParameterInfo.New (GetReturnType (method.mhandle), method, get_retval_marshal (method.mhandle));
		}
	};
	
	/*
	 * Note: most of this class needs to be duplicated for the contructor, since
	 * the .NET reflection class hierarchy is so broken.
	 */
	[Serializable()]
	[StructLayout (LayoutKind.Sequential)]
	internal class MonoMethod : MethodInfo, ISerializable
	{
#pragma warning disable 649
		internal IntPtr mhandle;
		string name;
		Type reftype;
#pragma warning restore 649

		internal MonoMethod () {
		}

		internal MonoMethod (RuntimeMethodHandle mhandle) {
			this.mhandle = mhandle.Value;
		}
		
		[MethodImplAttribute(MethodImplOptions.InternalCall)]
		internal static extern string get_name (MethodBase method);

		[MethodImplAttribute(MethodImplOptions.InternalCall)]
		internal static extern MonoMethod get_base_method (MonoMethod method, bool definition);

		public override MethodInfo GetBaseDefinition ()
		{
			return get_base_method (this, true);
		}

		internal override MethodInfo GetBaseMethod ()
		{
			return get_base_method (this, false);
		}

		public override ParameterInfo ReturnParameter {
			get {
				return MonoMethodInfo.GetReturnParameterInfo (this);
			}
		}

		public override Type ReturnType {
			get {
				return MonoMethodInfo.GetReturnType (mhandle);
			}
		}
		public override ICustomAttributeProvider ReturnTypeCustomAttributes { 
			get {
				return MonoMethodInfo.GetReturnParameterInfo (this);
			}
		}
		
		public override MethodImplAttributes GetMethodImplementationFlags ()
		{
			return MonoMethodInfo.GetMethodImplementationFlags (mhandle);
		}

		public override ParameterInfo[] GetParameters ()
		{
			var src = MonoMethodInfo.GetParametersInfo (mhandle, this);
			if (src.Length == 0)
				return src;

			// Have to clone because GetParametersInfo icall returns cached value
			var dest = new ParameterInfo [src.Length];
			Array.FastCopy (src, 0, dest, 0, src.Length);
			return dest;
		}

		internal override ParameterInfo[] GetParametersInternal ()
		{
			return MonoMethodInfo.GetParametersInfo (mhandle, this);
		}
		
		internal override int GetParametersCount ()
		{
			return MonoMethodInfo.GetParametersInfo (mhandle, this).Length;
		}

		/*
		 * InternalInvoke() receives the parameters correctly converted by the 
		 * binder to match the types of the method signature.
		 */
		[MethodImplAttribute(MethodImplOptions.InternalCall)]
		internal extern Object InternalInvoke (Object obj, Object[] parameters, out Exception exc);

		[DebuggerHidden]
		[DebuggerStepThrough]
		public override Object Invoke (Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) 
		{
			if (binder == null)
				binder = Binder.DefaultBinder;

			/*Avoid allocating an array every time*/
			ParameterInfo[] pinfo = GetParametersInternal ();
			binder.ConvertValues (parameters, pinfo, culture, (invokeAttr & BindingFlags.ExactBinding) != 0);

#if !NET_2_1
			if (SecurityManager.SecurityEnabled) {
				// sadly Attributes doesn't tell us which kind of security action this is so
				// we must do it the hard way - and it also means that we can skip calling
				// Attribute (which is another an icall)
				SecurityManager.ReflectedLinkDemandInvoke (this);
			}
#endif

			if (ContainsGenericParameters)
				throw new InvalidOperationException ("Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.");

			Exception exc;
			object o = null;

			try {
				// The ex argument is used to distinguish exceptions thrown by the icall
				// from the exceptions thrown by the called method (which need to be
				// wrapped in TargetInvocationException).
				o = InternalInvoke (obj, parameters, out exc);
			} catch (ThreadAbortException) {
				throw;
#if NET_2_1
			} catch (MethodAccessException) {
				throw;
#endif
			} catch (Exception e) {
				throw new TargetInvocationException (e);
			}

			if (exc != null)
				throw exc;
			return o;
		}

		public override RuntimeMethodHandle MethodHandle { 
			get {
				return new RuntimeMethodHandle (mhandle);
			} 
		}
		
		public override MethodAttributes Attributes { 
			get {
				return MonoMethodInfo.GetAttributes (mhandle);
			} 
		}

		public override CallingConventions CallingConvention { 
			get {
				return MonoMethodInfo.GetCallingConvention (mhandle);
			}
		}
		
		public override Type ReflectedType {
			get {
				return reftype;
			}
		}
		public override Type DeclaringType {
			get {
				return MonoMethodInfo.GetDeclaringType (mhandle);
			}
		}
		public override string Name {
			get {
				if (name != null)
					return name;
				return get_name (this);
			}
		}
		
		public override bool IsDefined (Type attributeType, bool inherit) {
			return MonoCustomAttrs.IsDefined (this, attributeType, inherit);
		}

		public override object[] GetCustomAttributes( bool inherit) {
			return MonoCustomAttrs.GetCustomAttributes (this, inherit);
		}
		public override object[] GetCustomAttributes( Type attributeType, bool inherit) {
			return MonoCustomAttrs.GetCustomAttributes (this, attributeType, inherit);
		}

		[MethodImplAttribute(MethodImplOptions.InternalCall)]
		internal static extern DllImportAttribute GetDllImportAttribute (IntPtr mhandle);

		internal object[] GetPseudoCustomAttributes ()
		{
			int count = 0;

			/* MS.NET doesn't report MethodImplAttribute */

			MonoMethodInfo info = MonoMethodInfo.GetMethodInfo (mhandle);
			if ((info.iattrs & MethodImplAttributes.PreserveSig) != 0)
				count ++;
			if ((info.attrs & MethodAttributes.PinvokeImpl) != 0)
				count ++;
			
			if (count == 0)
				return null;
			object[] attrs = new object [count];
			count = 0;

			if ((info.iattrs & MethodImplAttributes.PreserveSig) != 0)
				attrs [count ++] = new PreserveSigAttribute ();
			if ((info.attrs & MethodAttributes.PinvokeImpl) != 0) {
				DllImportAttribute attr = GetDllImportAttribute (mhandle);
				if ((info.iattrs & MethodImplAttributes.PreserveSig) != 0)
					attr.PreserveSig = true;
				attrs [count ++] = attr;
			}

			return attrs;
		}

		public override string ToString () {
			StringBuilder sb = new StringBuilder ();
			Type retType = ReturnType;
			if (Type.ShouldPrintFullName (retType))
				sb.Append (retType.ToString ());
			else
				sb.Append (retType.Name);
			sb.Append (" ");
			sb.Append (Name);
			if (IsGenericMethod) {
				Type[] gen_params = GetGenericArguments ();
				sb.Append ("[");
				for (int j = 0; j < gen_params.Length; j++) {
					if (j > 0)
						sb.Append (",");
					sb.Append (gen_params [j].Name);
				}
				sb.Append ("]");
			}
			sb.Append ("(");

			var p = GetParametersInternal ();
			ParameterInfo.FormatParameters (sb, p);

			if ((CallingConvention & CallingConventions.VarArgs) != 0) {
				if (p.Length > 0)
					sb.Append (", ");
				sb.Append ("...");
			}
			
			sb.Append (")");
			return sb.ToString ();
		}

	
		// ISerializable
		public void GetObjectData(SerializationInfo info, StreamingContext context) 
		{
			Type[] genericArguments = IsGenericMethod && !IsGenericMethodDefinition
				? GetGenericArguments () : null;
			MemberInfoSerializationHolder.Serialize ( info, Name, ReflectedType, ToString(), MemberTypes.Method, genericArguments);
		}

		public override MethodInfo MakeGenericMethod (Type [] methodInstantiation)
		{
			if (methodInstantiation == null)
				throw new ArgumentNullException ("methodInstantiation");

			if (!IsGenericMethodDefinition)
				throw new InvalidOperationException ("not a generic method definition");

			/*FIXME add GetGenericArgumentsLength() internal vcall to speed this up*/
			if (GetGenericArguments ().Length != methodInstantiation.Length)
				throw new ArgumentException ("Incorrect length");

			bool hasUserType = false;
			foreach (Type type in methodInstantiation) {
				if (type == null)
					throw new ArgumentNullException ();
				if (!(type is MonoType))
					hasUserType = true;
			}

			if (hasUserType)
#if FULL_AOT_RUNTIME
				throw new NotSupportedException ("User types are not supported under full aot");
#else
				return new MethodOnTypeBuilderInst (this, methodInstantiation);
#endif

			MethodInfo ret = MakeGenericMethod_impl (methodInstantiation);
			if (ret == null)
				throw new ArgumentException (String.Format ("The method has {0} generic parameter(s) but {1} generic argument(s) were provided.", GetGenericArguments ().Length, methodInstantiation.Length));
			return ret;
		}

		[MethodImplAttribute(MethodImplOptions.InternalCall)]
		extern MethodInfo MakeGenericMethod_impl (Type [] types);

		[MethodImplAttribute(MethodImplOptions.InternalCall)]
		public override extern Type [] GetGenericArguments ();

		[MethodImplAttribute(MethodImplOptions.InternalCall)]
		extern MethodInfo GetGenericMethodDefinition_impl ();

		public override MethodInfo GetGenericMethodDefinition ()
		{
			MethodInfo res = GetGenericMethodDefinition_impl ();
			if (res == null)
				throw new InvalidOperationException ();

			return res;
		}

		public override extern bool IsGenericMethodDefinition {
			[MethodImplAttribute(MethodImplOptions.InternalCall)]
			get;
		}

		public override extern bool IsGenericMethod {
			[MethodImplAttribute(MethodImplOptions.InternalCall)]
			get;
		}

		public override bool ContainsGenericParameters {
			get {
				if (IsGenericMethod) {
					foreach (Type arg in GetGenericArguments ())
						if (arg.ContainsGenericParameters)
							return true;
				}
				return DeclaringType.ContainsGenericParameters;
			}
		}

		public override MethodBody GetMethodBody () {
			return GetMethodBody (mhandle);
		}

#if NET_4_0
		public override IList<CustomAttributeData> GetCustomAttributesData () {
			return CustomAttributeData.GetCustomAttributes (this);
		}
#endif
	}
	
	[Serializable()]
	[StructLayout (LayoutKind.Sequential)]
	internal class MonoCMethod : ConstructorInfo, ISerializable
	{
#pragma warning disable 649		
		internal IntPtr mhandle;
		string name;
		Type reftype;
#pragma warning restore 649		
		
		public override MethodImplAttributes GetMethodImplementationFlags ()
		{
			return MonoMethodInfo.GetMethodImplementationFlags (mhandle);
		}

		public override ParameterInfo[] GetParameters ()
		{
			return MonoMethodInfo.GetParametersInfo (mhandle, this);
		}

		internal override ParameterInfo[] GetParametersInternal ()
		{
			return MonoMethodInfo.GetParametersInfo (mhandle, this);
		}		

		internal override int GetParametersCount ()
		{
			var pi = MonoMethodInfo.GetParametersInfo (mhandle, this);
			return pi == null ? 0 : pi.Length;
		}

		/*
		 * InternalInvoke() receives the parameters correctly converted by the binder
		 * to match the types of the method signature.
		 */
		[MethodImplAttribute(MethodImplOptions.InternalCall)]
		internal extern Object InternalInvoke (Object obj, Object[] parameters, out Exception exc);

		[DebuggerHidden]
		[DebuggerStepThrough]
		public override object Invoke (object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) 
		{
			if (obj == null) {
				if (!IsStatic)
					throw new TargetException ("Instance constructor requires a target");
			} else if (!DeclaringType.IsInstanceOfType (obj)) {
				throw new TargetException ("Constructor does not match target type");				
			}

			return DoInvoke (obj, invokeAttr, binder, parameters, culture);
		}

		object DoInvoke (object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) 
		{
			if (binder == null)
				binder = Binder.DefaultBinder;

			ParameterInfo[] pinfo = MonoMethodInfo.GetParametersInfo (mhandle, this);

			binder.ConvertValues (parameters, pinfo, culture, (invokeAttr & BindingFlags.ExactBinding) != 0);

#if !NET_2_1
			if (SecurityManager.SecurityEnabled) {
				// sadly Attributes doesn't tell us which kind of security action this is so
				// we must do it the hard way - and it also means that we can skip calling
				// Attribute (which is another an icall)
				SecurityManager.ReflectedLinkDemandInvoke (this);
			}
#endif

			if (obj == null && DeclaringType.ContainsGenericParameters)
				throw new MemberAccessException ("Cannot create an instance of " + DeclaringType + " because Type.ContainsGenericParameters is true.");

			if ((invokeAttr & BindingFlags.CreateInstance) != 0 && DeclaringType.IsAbstract) {
				throw new MemberAccessException (String.Format ("Cannot create an instance of {0} because it is an abstract class", DeclaringType));
			}

			return InternalInvoke (obj, parameters);
		}

		public object InternalInvoke (object obj, object[] parameters)
		{
			Exception exc;
			object o = null;

			try {
				o = InternalInvoke (obj, parameters, out exc);
#if NET_2_1
			} catch (MethodAccessException) {
				throw;
#endif
			} catch (Exception e) {
				throw new TargetInvocationException (e);
			}

			if (exc != null)
				throw exc;

			return obj == null ? o : null;
		}

		[DebuggerHidden]
		[DebuggerStepThrough]
		public override Object Invoke (BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
		{
			return DoInvoke (null, invokeAttr, binder, parameters, culture);
		}

		public override RuntimeMethodHandle MethodHandle { 
			get {
				return new RuntimeMethodHandle (mhandle);
			} 
		}
		
		public override MethodAttributes Attributes { 
			get {
				return MonoMethodInfo.GetAttributes (mhandle);
			} 
		}

		public override CallingConventions CallingConvention { 
			get {
				return MonoMethodInfo.GetCallingConvention (mhandle);
			}
		}
		
		public override Type ReflectedType {
			get {
				return reftype;
			}
		}
		public override Type DeclaringType {
			get {
				return MonoMethodInfo.GetDeclaringType (mhandle);
			}
		}
		public override string Name {
			get {
				if (name != null)
					return name;
				return MonoMethod.get_name (this);
			}
		}

		public override bool IsDefined (Type attributeType, bool inherit) {
			return MonoCustomAttrs.IsDefined (this, attributeType, inherit);
		}

		public override object[] GetCustomAttributes( bool inherit) {
			return MonoCustomAttrs.GetCustomAttributes (this, inherit);
		}

		public override object[] GetCustomAttributes( Type attributeType, bool inherit) {
			return MonoCustomAttrs.GetCustomAttributes (this, attributeType, inherit);
		}

		public override MethodBody GetMethodBody () {
			return GetMethodBody (mhandle);
		}

		public override string ToString () {
			StringBuilder sb = new StringBuilder ();
			sb.Append ("Void ");
			sb.Append (Name);
			sb.Append ("(");
			ParameterInfo[] p = GetParameters ();
			for (int i = 0; i < p.Length; ++i) {
				if (i > 0)
					sb.Append (", ");
				sb.Append (p[i].ParameterType.Name);
			}
			if (CallingConvention == CallingConventions.Any)
				sb.Append (", ...");
			sb.Append (")");
			return sb.ToString ();
		}

		// ISerializable
		public void GetObjectData(SerializationInfo info, StreamingContext context) 
		{
			MemberInfoSerializationHolder.Serialize ( info, Name, ReflectedType, ToString(), MemberTypes.Constructor);
		}

#if NET_4_0
		public override IList<CustomAttributeData> GetCustomAttributesData () {
			return CustomAttributeData.GetCustomAttributes (this);
		}
#endif
	}
}