context: /*
    Copyright (C) 2008-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.Collections.Generic;
  using System.Diagnostics;
  using System.Runtime.InteropServices;
  using IKVM.Reflection.Impl;
  using IKVM.Reflection.Metadata;
  using IKVM.Reflection.Writer;
  
  namespace IKVM.Reflection.Emit
  {
  	public sealed class GenericTypeParameterBuilder : Type
  	{
  		private readonly string name;
  		private readonly TypeBuilder type;
  		private readonly MethodBuilder method;
  		private readonly int paramToken;
  		private readonly int position;
  		private int typeToken;
  		private Type baseType;
  
  		internal GenericTypeParameterBuilder(string name, TypeBuilder type, MethodBuilder method, int position)
  		{
  			this.name = name;
  			this.type = type;
  			this.method = method;
  			this.position = position;
  			GenericParamTable.Record rec = new GenericParamTable.Record();
  			rec.Number = (short)position;
  			rec.Flags = 0;
  			rec.Owner = type != null ? type.MetadataToken : method.MetadataToken;
  			rec.Name = this.ModuleBuilder.Strings.Add(name);
  			this.paramToken = this.ModuleBuilder.GenericParam.AddRecord(rec);
  		}
  
  		public override string AssemblyQualifiedName
  		{
  			get { return null; }
  		}
  
  		public override bool IsValueType
  		{
  			get { return (this.GenericParameterAttributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0; }
  		}
  
  		public override Type BaseType
  		{
  			get { return baseType; }
  		}
  
  		public override Type[] __GetDeclaredInterfaces()
  		{
  			throw new NotImplementedException();
  		}
  
  		public override TypeAttributes Attributes
  		{
  			get { return TypeAttributes.Public; }
  		}
  
  		public override string Namespace
  		{
  			get { return DeclaringType.Namespace; }
  		}
  
  		public override Type UnderlyingSystemType
  		{
  			get { return this; }
  		}
  
  		public override string Name
  		{
  			get { return name; }
  		}
  
  		public override string FullName
  		{
  			get { return null; }
  		}
  
  		public override string ToString()
  		{
  			return this.Name;
  		}
  
  		private ModuleBuilder ModuleBuilder
  		{
  			get { return type != null ? type.ModuleBuilder : method.ModuleBuilder; }
  		}
  
  		public override Module Module
  		{
  			get { return ModuleBuilder; }
  		}
  
  		public override bool IsGenericParameter
  		{
  			get { return true; }
  		}
  
  		public override int GenericParameterPosition
  		{
  			get { return position; }
  		}
  
  		public override Type DeclaringType
  		{
  			get { return type; }
  		}
  
  		public override MethodBase DeclaringMethod
  		{
  			get { return method; }
  		}
  
  		public override Type[] GetGenericParameterConstraints()
  		{
  			throw new NotImplementedException();
  		}
  
  		public override GenericParameterAttributes GenericParameterAttributes
  		{
  			get
  			{
  				if (type != null)
  				{
  					type.CheckBaked();
  				}
  				else
  				{
  					method.CheckBaked();
  				}
  				return this.ModuleBuilder.GenericParam.GetAttributes(paramToken);
  			}
  		}
  
  		private void AddConstraint(Type type)
  		{
  			GenericParamConstraintTable.Record rec = new GenericParamConstraintTable.Record();
  			rec.Owner = paramToken;
  			rec.Constraint = this.ModuleBuilder.GetTypeTokenForMemberRef(type);
  			this.ModuleBuilder.GenericParamConstraint.AddRecord(rec);
  		}
  
  		public void SetBaseTypeConstraint(Type baseTypeConstraint)
  		{
  			this.baseType = baseTypeConstraint;
  			AddConstraint(baseTypeConstraint);
  		}
  
  		public void SetInterfaceConstraints(params Type[] interfaceConstraints)
  		{
  			foreach (Type type in interfaceConstraints)
  			{
  				AddConstraint(type);
  			}
  		}
  
  		public void SetGenericParameterAttributes(GenericParameterAttributes genericParameterAttributes)
  		{
  			// for now we'll back patch the table
  			this.ModuleBuilder.GenericParam.PatchAttribute(paramToken, genericParameterAttributes);
  		}
  
  		public void SetCustomAttribute(CustomAttributeBuilder customBuilder)
  		{
  			this.ModuleBuilder.SetCustomAttribute(GetModuleBuilderToken(), customBuilder);
  		}
  
  		public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute)
  		{
  			SetCustomAttribute(new CustomAttributeBuilder(con, binaryAttribute));
  		}
  
  		internal override int GetModuleBuilderToken()
  		{
  			if (typeToken == 0)
  			{
  				ByteBuffer spec = new ByteBuffer(5);
  				Signature.WriteTypeSpec(this.ModuleBuilder, spec, this);
  				typeToken = 0x1B000000 | this.ModuleBuilder.TypeSpec.AddRecord(this.ModuleBuilder.Blobs.Add(spec));
  			}
  			return typeToken;
  		}
  
  		internal override Type BindTypeParameters(IGenericBinder binder)
  		{
  			if (type != null)
  			{
  				return binder.BindTypeParameter(this);
  			}
  			else
  			{
  				return binder.BindMethodParameter(this);
  			}
  		}
  	}
  
  	public sealed class TypeBuilder : Type, ITypeOwner
  	{
  		public const int UnspecifiedTypeSize = 0;
  		private readonly ITypeOwner owner;
  		private readonly int token;
  		private int extends;
  		private Type baseType;
  		private readonly int typeName;
  		private readonly int typeNameSpace;
  		private readonly string nameOrFullName;
  		private readonly List<MethodBuilder> methods = new List<MethodBuilder>();
  		private readonly List<FieldBuilder> fields = new List<FieldBuilder>();
  		private List<PropertyBuilder> properties;
  		private List<EventBuilder> events;
  		private TypeAttributes attribs;
  		private TypeFlags typeFlags;
  		private GenericTypeParameterBuilder[] gtpb;
  		private List<CustomAttributeBuilder> declarativeSecurity;
  		private List<Type> interfaces;
  
  		[Flags]
  		private enum TypeFlags
  		{
  			IsGenericTypeDefinition = 1,
  			HasNestedTypes = 2,
  			Baked = 4,
  		}
  
  		internal TypeBuilder(ITypeOwner owner, string name, Type baseType, TypeAttributes attribs)
  		{
  			this.owner = owner;
  			this.token = this.ModuleBuilder.TypeDef.AllocToken();
  			this.nameOrFullName = TypeNameParser.Escape(name);
  			SetParent(baseType);
  			this.attribs = attribs;
  			if (!this.IsNested)
  			{
  				int lastdot = name.LastIndexOf('.');
  				if (lastdot > 0)
  				{
  					this.typeNameSpace = this.ModuleBuilder.Strings.Add(name.Substring(0, lastdot));
  					name = name.Substring(lastdot + 1);
  				}
  			}
  			this.typeName = this.ModuleBuilder.Strings.Add(name);
  		}
  
  		public ConstructorBuilder DefineDefaultConstructor(MethodAttributes attributes)
  		{
  			ConstructorBuilder cb = DefineConstructor(attributes, CallingConventions.Standard, Type.EmptyTypes);
  			ILGenerator ilgen = cb.GetILGenerator();
  			ilgen.Emit(OpCodes.Ldarg_0);
  			ilgen.Emit(OpCodes.Call, baseType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null));
  			ilgen.Emit(OpCodes.Ret);
  			return cb;
  		}
  
  		public ConstructorBuilder DefineConstructor(MethodAttributes attribs, CallingConventions callConv, Type[] parameterTypes)
  		{
  			return DefineConstructor(attribs, callConv, parameterTypes, null, null);
  		}
  
  		public ConstructorBuilder DefineConstructor(MethodAttributes attribs, CallingConventions callingConvention, Type[] parameterTypes, Type[][] requiredCustomModifiers, Type[][] optionalCustomModifiers)
  		{
  			attribs |= MethodAttributes.RTSpecialName | MethodAttributes.SpecialName;
  			string name = (attribs & MethodAttributes.Static) == 0 ? ConstructorInfo.ConstructorName : ConstructorInfo.TypeConstructorName;
  			MethodBuilder mb = DefineMethod(name, attribs, callingConvention, null, null, null, parameterTypes, requiredCustomModifiers, optionalCustomModifiers);
  			return new ConstructorBuilder(mb);
  		}
  
  		public ConstructorBuilder DefineTypeInitializer()
  		{
  			MethodBuilder mb = DefineMethod(ConstructorInfo.TypeConstructorName, MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName, null, Type.EmptyTypes);
  			return new ConstructorBuilder(mb);
  		}
  
  		private MethodBuilder CreateMethodBuilder(string name, MethodAttributes attributes, CallingConventions callingConvention)
  		{
  			this.ModuleBuilder.MethodDef.AddVirtualRecord();
  			MethodBuilder mb = new MethodBuilder(this, name, attributes, callingConvention);
  			methods.Add(mb);
  			return mb;
  		}
  
  		public MethodBuilder DefineMethod(string name, MethodAttributes attribs)
  		{
  			return DefineMethod(name, attribs, CallingConventions.Standard);
  		}
  
  		public MethodBuilder DefineMethod(string name, MethodAttributes attribs, CallingConventions callingConvention)
  		{
  			return CreateMethodBuilder(name, attribs, callingConvention);
  		}
  
  		public MethodBuilder DefineMethod(string name, MethodAttributes attribs, Type returnType, Type[] parameterTypes)
  		{
  			return DefineMethod(name, attribs, CallingConventions.Standard, returnType, null, null, parameterTypes, null, null);
  		}
  
  		public MethodBuilder DefineMethod(string name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes)
  		{
  			return DefineMethod(name, attributes, callingConvention, returnType, null, null, parameterTypes, null, null);
  		}
  
  		public MethodBuilder DefineMethod(string name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] returnTypeRequiredCustomModifiers, Type[] returnTypeOptionalCustomModifiers, Type[] parameterTypes, Type[][] parameterTypeRequiredCustomModifiers, Type[][] parameterTypeOptionalCustomModifiers)
  		{
  			MethodBuilder mb = CreateMethodBuilder(name, attributes, callingConvention);
  			mb.SetSignature(returnType, returnTypeRequiredCustomModifiers, returnTypeOptionalCustomModifiers, parameterTypes, parameterTypeRequiredCustomModifiers, parameterTypeOptionalCustomModifiers);
  			return mb;
  		}
  
  		public MethodBuilder DefinePInvokeMethod(string name, string dllName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet)
  		{
  			return DefinePInvokeMethod(name, dllName, null, attributes, callingConvention, returnType, null, null, parameterTypes, null, null, nativeCallConv, nativeCharSet);
  		}
  
  		public MethodBuilder DefinePInvokeMethod(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet)
  		{
  			return DefinePInvokeMethod(name, dllName, entryName, attributes, callingConvention, returnType, null, null, parameterTypes, null, null, nativeCallConv, nativeCharSet);
  		}
  
  		public MethodBuilder DefinePInvokeMethod(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention,
  			Type returnType, Type[] returnTypeRequiredCustomModifiers, Type[] returnTypeOptionalCustomModifiers,
  			Type[] parameterTypes, Type[][] parameterTypeRequiredCustomModifiers, Type[][] parameterTypeOptionalCustomModifiers,
  			CallingConvention nativeCallConv, CharSet nativeCharSet)
  		{
  			MethodBuilder mb = DefineMethod(name, attributes | MethodAttributes.PinvokeImpl, callingConvention,
  				returnType, returnTypeRequiredCustomModifiers, returnTypeOptionalCustomModifiers,
  				parameterTypes, parameterTypeRequiredCustomModifiers, parameterTypeOptionalCustomModifiers);
  			mb.SetDllImportPseudoCustomAttribute(dllName, entryName, nativeCallConv, nativeCharSet, null, null, null, null, null);
  			return mb;
  		}
  
  		public void DefineMethodOverride(MethodInfo methodInfoBody, MethodInfo methodInfoDeclaration)
  		{
  			MethodImplTable.Record rec = new MethodImplTable.Record();
  			rec.Class = token;
  			rec.MethodBody = this.ModuleBuilder.GetMethodToken(methodInfoBody).Token;
  			rec.MethodDeclaration = this.ModuleBuilder.GetMethodToken(methodInfoDeclaration).Token;
  			this.ModuleBuilder.MethodImpl.AddRecord(rec);
  		}
  
  		public FieldBuilder DefineField(string name, Type fieldType, FieldAttributes attribs)
  		{
  			return DefineField(name, fieldType, null, null, attribs);
  		}
  
  		public FieldBuilder DefineField(string fieldName, Type type, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers, FieldAttributes attributes)
  		{
  			FieldBuilder fb = new FieldBuilder(this, fieldName, type, requiredCustomModifiers, optionalCustomModifiers, attributes);
  			fields.Add(fb);
  			return fb;
  		}
  
  		public PropertyBuilder DefineProperty(string name, PropertyAttributes attributes, Type returnType, Type[] parameterTypes)
  		{
  			return DefineProperty(name, attributes, returnType, null, null, parameterTypes, null, null);
  		}
  
  		public PropertyBuilder DefineProperty(string name, PropertyAttributes attributes, Type returnType, Type[] returnTypeRequiredCustomModifiers, Type[] returnTypeOptionalCustomModifiers,
  			Type[] parameterTypes, Type[][] parameterTypeRequiredCustomModifiers, Type[][] parameterTypeOptionalCustomModifiers)
  		{
  			return DefinePropertyImpl(name, attributes, CallingConventions.Standard, true, returnType, returnTypeRequiredCustomModifiers, returnTypeOptionalCustomModifiers,
  				parameterTypes, parameterTypeRequiredCustomModifiers, parameterTypeOptionalCustomModifiers);
  		}
  
  		public PropertyBuilder DefineProperty(string name, PropertyAttributes attributes, CallingConventions callingConvention,
  			Type returnType, Type[] returnTypeRequiredCustomModifiers, Type[] returnTypeOptionalCustomModifiers,
  			Type[] parameterTypes, Type[][] parameterTypeRequiredCustomModifiers, Type[][] parameterTypeOptionalCustomModifiers)
  		{
  			return DefinePropertyImpl(name, attributes, callingConvention, false, returnType, returnTypeRequiredCustomModifiers, returnTypeOptionalCustomModifiers,
  				parameterTypes, parameterTypeRequiredCustomModifiers, parameterTypeOptionalCustomModifiers);
  		}
  
  		private PropertyBuilder DefinePropertyImpl(string name, PropertyAttributes attributes, CallingConventions callingConvention, bool patchCallingConvention,
  			Type returnType, Type[] returnTypeRequiredCustomModifiers, Type[] returnTypeOptionalCustomModifiers,
  			Type[] parameterTypes, Type[][] parameterTypeRequiredCustomModifiers, Type[][] parameterTypeOptionalCustomModifiers)
  		{
  			if (properties == null)
  			{
  				properties = new List<PropertyBuilder>();
  			}
  			PropertySignature sig = PropertySignature.Create(callingConvention, returnType, returnTypeOptionalCustomModifiers, returnTypeRequiredCustomModifiers,
  				parameterTypes, parameterTypeOptionalCustomModifiers, parameterTypeRequiredCustomModifiers);
  			PropertyBuilder pb = new PropertyBuilder(this, name, attributes, sig, patchCallingConvention);
  			properties.Add(pb);
  			return pb;
  		}
  
  		public EventBuilder DefineEvent(string name, EventAttributes attributes, Type eventtype)
  		{
  			if (events == null)
  			{
  				events = new List<EventBuilder>();
  			}
  			EventBuilder eb = new EventBuilder(this, name, attributes, eventtype);
  			events.Add(eb);
  			return eb;
  		}
  
  		public TypeBuilder DefineNestedType(string name)
  		{
  			return DefineNestedType(name, TypeAttributes.Class | TypeAttributes.NestedPrivate);
  		}
  
  		public TypeBuilder DefineNestedType(string name, TypeAttributes attribs)
  		{
  			return DefineNestedType(name, attribs, null);
  		}
  
  		public TypeBuilder DefineNestedType(string name, TypeAttributes attr, Type parent, Type[] interfaces)
  		{
  			TypeBuilder tb = DefineNestedType(name, attr, parent);
  			foreach (Type iface in interfaces)
  			{
  				tb.AddInterfaceImplementation(iface);
  			}
  			return tb;
  		}
  
  		public TypeBuilder DefineNestedType(string name, TypeAttributes attr, Type parent)
  		{
  			this.typeFlags |= TypeFlags.HasNestedTypes;
  			return this.ModuleBuilder.DefineNestedTypeHelper(this, name, attr, parent, PackingSize.Unspecified, 0);
  		}
  
  		public TypeBuilder DefineNestedType(string name, TypeAttributes attr, Type parent, int typeSize)
  		{
  			this.typeFlags |= TypeFlags.HasNestedTypes;
  			return this.ModuleBuilder.DefineNestedTypeHelper(this, name, attr, parent, PackingSize.Unspecified, typeSize);
  		}
  
  		public TypeBuilder DefineNestedType(string name, TypeAttributes attr, Type parent, PackingSize packSize)
  		{
  			this.typeFlags |= TypeFlags.HasNestedTypes;
  			return this.ModuleBuilder.DefineNestedTypeHelper(this, name, attr, parent, packSize, 0);
  		}
  
  		public void SetParent(Type parent)
  		{
  			baseType = parent;
  			if (parent == null)
  			{
  				extends = 0;
  			}
  			else
  			{
  				extends = this.ModuleBuilder.GetTypeToken(parent).Token;
  			}
  		}
  
  		public void AddInterfaceImplementation(Type interfaceType)
  		{
  			if (interfaces == null)
  			{
  				interfaces = new List<Type>();
  			}
  			interfaces.Add(interfaceType);
  			InterfaceImplTable.Record rec = new InterfaceImplTable.Record();
  			rec.Class = token;
  			rec.Interface = this.ModuleBuilder.GetTypeToken(interfaceType).Token;
  			this.ModuleBuilder.InterfaceImpl.AddRecord(rec);
  		}
  
  		public int Size
  		{
  			get
  			{
  				for (int i = 0; i < this.ModuleBuilder.ClassLayout.records.Length; i++)
  				{
  					if (this.ModuleBuilder.ClassLayout.records[i].Parent == token)
  					{
  						return this.ModuleBuilder.ClassLayout.records[i].ClassSize;
  					}
  				}
  				return 0;
  			}
  		}
  
  		public PackingSize PackingSize
  		{
  			get
  			{
  				for (int i = 0; i < this.ModuleBuilder.ClassLayout.records.Length; i++)
  				{
  					if (this.ModuleBuilder.ClassLayout.records[i].Parent == token)
  					{
  						return (PackingSize)this.ModuleBuilder.ClassLayout.records[i].PackingSize;
  					}
  				}
  				return PackingSize.Unspecified;
  			}
  		}
  
  		private void SetStructLayoutPseudoCustomAttribute(CustomAttributeBuilder customBuilder)
  		{
  			object val = customBuilder.GetConstructorArgument(0);
  			LayoutKind layout;
  			if (val is short)
  			{
  				layout = (LayoutKind)(short)val;
  			}
  			else
  			{
  				layout = (LayoutKind)val;
  			}
  			int? pack = (int?)customBuilder.GetFieldValue("Pack");
  			int? size = (int?)customBuilder.GetFieldValue("Size");
  			if (pack.HasValue || size.HasValue)
  			{
  				ClassLayoutTable.Record rec = new ClassLayoutTable.Record();
  				rec.PackingSize = (short)(pack ?? 0);
  				rec.ClassSize = size ?? 0;
  				rec.Parent = token;
  				this.ModuleBuilder.ClassLayout.AddRecord(rec);
  			}
  			attribs &= ~TypeAttributes.LayoutMask;
  			switch (layout)
  			{
  				case LayoutKind.Auto:
  					attribs |= TypeAttributes.AutoLayout;
  					break;
  				case LayoutKind.Explicit:
  					attribs |= TypeAttributes.ExplicitLayout;
  					break;
  				case LayoutKind.Sequential:
  					attribs |= TypeAttributes.SequentialLayout;
  					break;
  			}
  			CharSet? charSet = customBuilder.GetFieldValue<CharSet>("CharSet");
  
gt: 			attribs &= ~TypeAttributes.StringFormatMask;