//
// ReflectionWriter.cs
//
// Author:
//   Jb Evain (jbevain@gmail.com)
//
// (C) 2005 - 2007 Jb Evain
//
// 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.
//

namespace Mono.Cecil {

	using System;
	using System.Collections;
	using System.Globalization;
	using System.Text;

	using Mono.Cecil.Binary;
	using Mono.Cecil.Cil;
	using Mono.Cecil.Metadata;
	using Mono.Cecil.Signatures;

	internal sealed class ReflectionWriter : BaseReflectionVisitor {

		StructureWriter m_structureWriter;
		ModuleDefinition m_mod;
		SignatureWriter m_sigWriter;
		CodeWriter m_codeWriter;
		MetadataWriter m_mdWriter;
		MetadataTableWriter m_tableWriter;
		MetadataRowWriter m_rowWriter;

		bool m_saveSymbols;
		string m_asmOutput;
		ISymbolWriter m_symbolWriter;

		ArrayList m_typeDefStack;
		ArrayList m_methodStack;
		ArrayList m_fieldStack;
		ArrayList m_genericParamStack;
		IDictionary m_typeSpecTokenCache;
		IDictionary m_memberRefTokenCache;

		uint m_methodIndex;
		uint m_fieldIndex;
		uint m_paramIndex;
		uint m_eventIndex;
		uint m_propertyIndex;

		MemoryBinaryWriter m_constWriter;

		public StructureWriter StructureWriter {
			get { return m_structureWriter; }
			set {
				 m_structureWriter = value;

				Initialize ();
			}
		}

		public CodeWriter CodeWriter {
			get { return m_codeWriter; }
		}

		public bool SaveSymbols {
			get { return m_saveSymbols; }
			set { m_saveSymbols = value; }
		}

		public string OutputFile
		{
			get { return m_asmOutput; }
			set { m_asmOutput = value; }
		}

		public ISymbolWriter SymbolWriter {
			get { return m_symbolWriter; }
			set { m_symbolWriter = value; }
		}

		public SignatureWriter SignatureWriter {
			get { return m_sigWriter; }
		}

		public MetadataWriter MetadataWriter {
			get { return m_mdWriter; }
		}

		public MetadataTableWriter MetadataTableWriter {
			get { return m_tableWriter; }
		}

		public MetadataRowWriter MetadataRowWriter {
			get { return m_rowWriter; }
		}

		public ReflectionWriter (ModuleDefinition mod)
		{
			m_mod = mod;
		}

		void Initialize ()
		{
			m_mdWriter = new MetadataWriter (
				m_mod.Assembly,
				m_mod.Image.MetadataRoot,
				m_structureWriter.Assembly.Kind,
				m_mod.Assembly.Runtime,
				m_structureWriter.GetWriter ());
			m_tableWriter = m_mdWriter.GetTableVisitor ();
			m_rowWriter = m_tableWriter.GetRowVisitor () as MetadataRowWriter;
			m_sigWriter = new SignatureWriter (m_mdWriter);
			m_codeWriter = new CodeWriter (this, m_mdWriter.CilWriter);

			m_typeDefStack = new ArrayList ();
			m_methodStack = new ArrayList ();
			m_fieldStack = new ArrayList ();
			m_genericParamStack = new ArrayList ();
			m_typeSpecTokenCache = new Hashtable ();
			m_memberRefTokenCache = new Hashtable ();

			m_methodIndex = 1;
			m_fieldIndex = 1;
			m_paramIndex = 1;
			m_eventIndex = 1;
			m_propertyIndex = 1;

			m_constWriter = new MemoryBinaryWriter ();
		}

		public TypeReference GetCoreType (string name)
		{
			return m_mod.Controller.Reader.SearchCoreType (name);
		}

		public static uint GetRidFor (IMetadataTokenProvider tp)
		{
			return tp.MetadataToken.RID;
		}

		public uint GetRidFor (AssemblyNameReference asmName)
		{
			return (uint) m_mod.AssemblyReferences.IndexOf (asmName) + 1;
		}

		public uint GetRidFor (ModuleDefinition mod)
		{
			return (uint) m_mod.Assembly.Modules.IndexOf (mod) + 1;
		}

		public uint GetRidFor (ModuleReference modRef)
		{
			return (uint) m_mod.ModuleReferences.IndexOf (modRef) + 1;
		}

		static bool IsTypeSpec (TypeReference type)
		{
			return type is TypeSpecification || type is GenericParameter;
		}

		public MetadataToken GetTypeDefOrRefToken (TypeReference type)
		{
			if (IsTypeSpec (type)) {
				uint sig = m_sigWriter.AddTypeSpec (GetTypeSpecSig (type));
				if (m_typeSpecTokenCache.Contains (sig))
					return (MetadataToken) m_typeSpecTokenCache [sig];

				TypeSpecTable tsTable = m_tableWriter.GetTypeSpecTable ();
				TypeSpecRow tsRow = m_rowWriter.CreateTypeSpecRow (sig);
				tsTable.Rows.Add (tsRow);

				MetadataToken token = new MetadataToken (TokenType.TypeSpec, (uint) tsTable.Rows.Count);
				if (! (type is GenericParameter))
					type.MetadataToken = token;

				m_typeSpecTokenCache [sig] = token;
				return token;
			} else if (type != null)
				return type.MetadataToken;
			else // <Module> and interfaces
				return new MetadataToken (TokenType.TypeRef, 0);
		}

		public MetadataToken GetMemberRefToken (MemberReference member)
		{
			if (member is MethodSpecification)
				return GetMemberRefToken (((MethodSpecification) member).ElementMethod);
			if (member is IMemberDefinition)
				return member.MetadataToken;
			if (m_memberRefTokenCache.Contains (member))
				return (MetadataToken) m_memberRefTokenCache [member];

			MemberRefTable mrTable = m_tableWriter.GetMemberRefTable ();

			uint sig = 0;
			if (member is FieldReference)
				sig = m_sigWriter.AddFieldSig (GetFieldSig ((FieldReference) member));
			else if (member is MethodReference)
				sig = m_sigWriter.AddMethodRefSig (GetMethodRefSig ((MethodReference) member));

			MetadataToken declaringType = GetTypeDefOrRefToken (member.DeclaringType);
			uint name = m_mdWriter.AddString (member.Name);

			for (int i = 0; i < mrTable.Rows.Count; i++) {
				MemberRefRow row = mrTable [i];
				if (row.Class == declaringType && row.Name == name && row.Signature == sig)
					return MetadataToken.FromMetadataRow (TokenType.MemberRef, i);
			}

			MemberRefRow mrRow = m_rowWriter.CreateMemberRefRow (
				declaringType,
				name,
				sig);

			mrTable.Rows.Add (mrRow);
			member.MetadataToken = new MetadataToken (
				TokenType.MemberRef, (uint) mrTable.Rows.Count);
			m_memberRefTokenCache [member] = member.MetadataToken;
			return member.MetadataToken;
		}

		public MetadataToken GetMethodSpecToken (GenericInstanceMethod gim)
		{
			uint sig = m_sigWriter.AddMethodSpec (GetMethodSpecSig (gim));
			MethodSpecTable msTable = m_tableWriter.GetMethodSpecTable ();

			MetadataToken meth = GetMemberRefToken (gim.ElementMethod);

			for (int i = 0; i < msTable.Rows.Count; i++) {
				MethodSpecRow row = msTable [i];
				if (row.Method == meth && row.Instantiation == sig)
					return MetadataToken.FromMetadataRow (TokenType.MethodSpec, i);
			}

			MethodSpecRow msRow = m_rowWriter.CreateMethodSpecRow (
				meth,
				sig);
			msTable.Rows.Add (msRow);
			gim.MetadataToken = new MetadataToken (TokenType.MethodSpec, (uint) msTable.Rows.Count);
			return gim.MetadataToken;
		}

		public override void VisitModuleDefinition (ModuleDefinition mod)
		{
			mod.FullLoad ();
		}

		public override void VisitTypeDefinitionCollection (TypeDefinitionCollection types)
		{
			TypeDefTable tdTable = m_tableWriter.GetTypeDefTable ();

			if (types [Constants.ModuleType] == null)
				types.Add (new TypeDefinition (
						Constants.ModuleType, string.Empty, TypeAttributes.NotPublic));

			foreach (TypeDefinition t in types)
				m_typeDefStack.Add (t);

			m_typeDefStack.Sort (TableComparers.TypeDef.Instance);

			for (int i = 0; i < m_typeDefStack.Count; i++) {
				TypeDefinition t = (TypeDefinition) m_typeDefStack [i];
				if (t.Module.Assembly != m_mod.Assembly)
					throw new ReflectionException ("A type as not been correctly imported");

				t.MetadataToken = new MetadataToken (TokenType.TypeDef, (uint) (i + 1));
			}

			foreach (TypeDefinition t in m_typeDefStack) {
				TypeDefRow tdRow = m_rowWriter.CreateTypeDefRow (
					t.Attributes,
					m_mdWriter.AddString (t.Name),
					m_mdWriter.AddString (t.Namespace),
					GetTypeDefOrRefToken (t.BaseType),
					0,
					0);

				tdTable.Rows.Add (tdRow);
			}
		}

		public void CompleteTypeDefinitions ()
		{
			TypeDefTable tdTable = m_tableWriter.GetTypeDefTable ();

			for (int i = 0; i < m_typeDefStack.Count; i++) {
				TypeDefRow tdRow = tdTable [i];
				TypeDefinition t = (TypeDefinition) m_typeDefStack [i];
				tdRow.FieldList = m_fieldIndex;
				tdRow.MethodList = m_methodIndex;
				if (t.HasFields) {
					foreach (FieldDefinition field in t.Fields)
						VisitFieldDefinition (field);
				}
				if (t.HasConstructors) {
					foreach (MethodDefinition ctor in t.Constructors)
						VisitMethodDefinition (ctor);
				}
				if (t.HasMethods) {
					foreach (MethodDefinition meth in t.Methods)
						VisitMethodDefinition (meth);
				}

				if (t.HasLayoutInfo)
					WriteLayout (t);
			}

			foreach (FieldDefinition field in m_fieldStack) {
				if (field.HasCustomAttributes)
					VisitCustomAttributeCollection (field.CustomAttributes);
				if (field.MarshalSpec != null)
					VisitMarshalSpec (field.MarshalSpec);
			}

			foreach (MethodDefinition meth in m_methodStack) {
				if (meth.ReturnType.HasCustomAttributes)
					VisitCustomAttributeCollection (meth.ReturnType.CustomAttributes);
				if (meth.HasParameters) {
					foreach (ParameterDefinition param in meth.Parameters) {
						if (param.HasCustomAttributes)
							VisitCustomAttributeCollection (param.CustomAttributes);
					}
				}
				if (meth.HasGenericParameters)
					VisitGenericParameterCollection (meth.GenericParameters);
				if (meth.HasOverrides)
					VisitOverrideCollection (meth.Overrides);
				if (meth.HasCustomAttributes)
					VisitCustomAttributeCollection (meth.CustomAttributes);
				if (meth.HasSecurityDeclarations)
					VisitSecurityDeclarationCollection (meth.SecurityDeclarations);
				if (meth.PInvokeInfo != null) {
					meth.Attributes |= MethodAttributes.PInvokeImpl;
					VisitPInvokeInfo (meth.PInvokeInfo);
				}
			}

			foreach (TypeDefinition t in m_typeDefStack)
				t.Accept (this);
		}

		public override void VisitTypeReferenceCollection (TypeReferenceCollection refs)
		{
			ArrayList orderedTypeRefs = new ArrayList (refs.Count);
			foreach (TypeReference tr in refs)
				orderedTypeRefs.Add (tr);

			orderedTypeRefs.Sort (TableComparers.TypeRef.Instance);

			TypeRefTable trTable = m_tableWriter.GetTypeRefTable ();
			foreach (TypeReference t in orderedTypeRefs) {
				MetadataToken scope;

				if (t.Module.Assembly != m_mod.Assembly)
					throw new ReflectionException ("A type as not been correctly imported");

				if (t.Scope == null)
					continue;

				if (t.DeclaringType != null)
					scope = new MetadataToken (TokenType.TypeRef, GetRidFor (t.DeclaringType));
				else if (t.Scope is AssemblyNameReference)
					scope = new MetadataToken (TokenType.AssemblyRef,
						GetRidFor ((AssemblyNameReference) t.Scope));
				else if (t.Scope is ModuleDefinition)
					scope = new MetadataToken (TokenType.Module,
						GetRidFor ((ModuleDefinition) t.Scope));
				else if (t.Scope is ModuleReference)
					scope = new MetadataToken (TokenType.ModuleRef,
						GetRidFor ((ModuleReference) t.Scope));
				else
					scope = new MetadataToken (TokenType.ExportedType, 0);

				TypeRefRow trRow = m_rowWriter.CreateTypeRefRow (
					scope,
					m_mdWriter.AddString (t.Name),
					m_mdWriter.AddString (t.Namespace));

				trTable.Rows.Add (trRow);
				t.MetadataToken = new MetadataToken (TokenType.TypeRef, (uint) trTable.Rows.Count);
			}
		}

		public override void VisitGenericParameterCollection (GenericParameterCollection parameters)
		{
			if (parameters.Count == 0)
				return;

			foreach (GenericParameter gp in parameters)
				m_genericParamStack.Add (gp);
		}

		public override void VisitInterfaceCollection (InterfaceCollection interfaces)
		{
			if (interfaces.Count == 0)
				return;

			InterfaceImplTable iiTable = m_tableWriter.GetInterfaceImplTable ();
			foreach (TypeReference interf in interfaces) {
				InterfaceImplRow iiRow = m_rowWriter.CreateInterfaceImplRow (
					GetRidFor (interfaces.Container),
					GetTypeDefOrRefToken (interf));

				iiTable.Rows.Add (iiRow);
			}
		}

		public override void VisitExternTypeCollection (ExternTypeCollection externs)
		{
			VisitCollection (externs);
		}

		public override void VisitExternType (TypeReference externType)
		{
			// TODO
		}

		public override void VisitOverrideCollection (OverrideCollection meths)
		{
			if (meths.Count == 0)
				return;

			MethodImplTable miTable = m_tableWriter.GetMethodImplTable ();
			foreach (MethodReference ov in meths) {
				MethodImplRow miRow = m_rowWriter.CreateMethodImplRow (
					GetRidFor (meths.Container.DeclaringType as TypeDefinition),
					new MetadataToken (TokenType.Method, GetRidFor (meths.Container)),
					GetMemberRefToken (ov));

				miTable.Rows.Add (miRow);
			}
		}

		public override void VisitNestedTypeCollection (NestedTypeCollection nestedTypes)
		{
			if (nestedTypes.Count == 0)
				return;

			NestedClassTable ncTable = m_tableWriter.GetNestedClassTable ();
			foreach (TypeDefinition nested in nestedTypes) {
				NestedClassRow ncRow = m_rowWriter.CreateNestedClassRow (
					nested.MetadataToken.RID,
					GetRidFor (nestedTypes.Container));

				ncTable.Rows.Add (ncRow);
			}
		}

		public override void VisitParameterDefinitionCollection (ParameterDefinitionCollection parameters)
		{
			if (parameters.Count == 0)
				return;

			ushort seq = 1;
			ParamTable pTable = m_tableWriter.GetParamTable ();
			foreach (ParameterDefinition param in parameters)
				InsertParameter (pTable, param, seq++);
		}

		void InsertParameter (ParamTable pTable, ParameterDefinition param, ushort seq)
		{
			ParamRow pRow = m_rowWriter.CreateParamRow (
				param.Attributes,
				seq,
				m_mdWriter.AddString (param.Name));

			pTable.Rows.Add (pRow);
			param.MetadataToken = new MetadataToken (TokenType.Param, (uint) pTable.Rows.Count);

			if (param.MarshalSpec != null)
				param.MarshalSpec.Accept (this);

			if (param.HasConstant)
				WriteConstant (param, param.ParameterType);

			m_paramIndex++;
		}

		static bool RequiresParameterRow (MethodReturnType mrt)
		{
			return mrt.HasConstant || mrt.MarshalSpec != null ||
				mrt.CustomAttributes.Count > 0 || mrt.Parameter.Attributes != (ParameterAttributes) 0;
		}

		public override void VisitMethodDefinition (MethodDefinition method)
		{
			MethodTable mTable = m_tableWriter.GetMethodTable ();
			MethodRow mRow = m_rowWriter.CreateMethodRow (
				RVA.Zero,
				method.ImplAttributes,
				method.Attributes,
				m_mdWriter.AddString (method.Name),
				m_sigWriter.AddMethodDefSig (GetMethodDefSig (method)),
				m_paramIndex);

			mTable.Rows.Add (mRow);
			m_methodStack.Add (method);
			method.MetadataToken = new MetadataToken (TokenType.Method, (uint) mTable.Rows.Count);
			m_methodIndex++;

			if (RequiresParameterRow (method.ReturnType))
				InsertParameter (m_tableWriter.GetParamTable (), method.ReturnType.Parameter, 0);

			VisitParameterDefinitionCollection (method.Parameters);
		}

		public override void VisitPInvokeInfo (PInvokeInfo pinvk)
		{
			ImplMapTable imTable = m_tableWriter.GetImplMapTable ();
			ImplMapRow imRow = m_rowWriter.CreateImplMapRow (
				pinvk.Attributes,
				new MetadataToken (TokenType.Method, GetRidFor (pinvk.Method)),
				m_mdWriter.AddString (pinvk.EntryPoint),
				GetRidFor (pinvk.Module));

			imTable.Rows.Add (imRow);
		}

		public override void VisitEventDefinitionCollection (EventDefinitionCollection events)
		{
			if (events.Count == 0)
				return;

			EventMapTable emTable = m_tableWriter.GetEventMapTable ();
			EventMapRow emRow = m_rowWriter.CreateEventMapRow (
				GetRidFor (events.Container),
				m_eventIndex);

			emTable.Rows.Add (emRow);
			VisitCollection (events);
		}

		public override void VisitEventDefinition (EventDefinition evt)
		{
			EventTable eTable = m_tableWriter.GetEventTable ();
			EventRow eRow = m_rowWriter.CreateEventRow (
				evt.Attributes,
				m_mdWriter.AddString (evt.Name),
				GetTypeDefOrRefToken (evt.EventType));

			eTable.Rows.Add (eRow);
			evt.MetadataToken = new MetadataToken (TokenType.Event, (uint) eTable.Rows.Count);

			if (evt.AddMethod != null)
				WriteSemantic (MethodSemanticsAttributes.AddOn, evt, evt.AddMethod);

			if (evt.InvokeMethod != null)
				WriteSemantic (MethodSemanticsAttributes.Fire, evt, evt.InvokeMethod);

			if (evt.RemoveMethod != null)
				WriteSemantic (MethodSemanticsAttributes.RemoveOn, evt, evt.RemoveMethod);

			m_eventIndex++;
		}

		public override void VisitFieldDefinition (FieldDefinition field)
		{
			FieldTable fTable = m_tableWriter.GetFieldTable ();
			FieldRow fRow = m_rowWriter.CreateFieldRow (
				field.Attributes,
				m_mdWriter.AddString (field.Name),
				m_sigWriter.AddFieldSig (GetFieldSig (field)));

			fTable.Rows.Add (fRow);
			field.MetadataToken = new MetadataToken (TokenType.Field, (uint) fTable.Rows.Count);
			m_fieldIndex++;

			if (field.HasConstant)
				WriteConstant (field, field.FieldType);

			if (field.HasLayoutInfo)
				WriteLayout (field);

			m_fieldStack.Add (field);
		}

		public override void VisitPropertyDefinitionCollection (PropertyDefinitionCollection properties)
		{
			if (properties.Count == 0)
				return;

			PropertyMapTable pmTable = m_tableWriter.GetPropertyMapTable ();
			PropertyMapRow pmRow = m_rowWriter.CreatePropertyMapRow (
				GetRidFor (properties.Container),
				m_propertyIndex);

			pmTable.Rows.Add (pmRow);
			VisitCollection (properties);
		}

		public override void VisitPropertyDefinition (PropertyDefinition property)
		{
			PropertyTable pTable = m_tableWriter.GetPropertyTable ();
			PropertyRow pRow = m_rowWriter.CreatePropertyRow (
				property.Attributes,
				m_mdWriter.AddString (property.Name),
				m_sigWriter.AddPropertySig (GetPropertySig (property)));

			pTable.Rows.Add (pRow);
			property.MetadataToken = new MetadataToken (TokenType.Property, (uint) pTable.Rows.Count);

			if (property.GetMethod != null)
				WriteSemantic (MethodSemanticsAttributes.Getter, property, property.GetMethod);

			if (property.SetMethod != null)
				WriteSemantic (MethodSemanticsAttributes.Setter, property, property.SetMethod);

			if (property.HasConstant)
				WriteConstant (property, property.PropertyType);

			m_propertyIndex++;
		}

		public override void VisitSecurityDeclarationCollection (SecurityDeclarationCollection secDecls)
		{
			if (secDecls.Count == 0)
				return;

			DeclSecurityTable dsTable = m_tableWriter.GetDeclSecurityTable ();
			foreach (SecurityDeclaration secDec in secDecls) {
				DeclSecurityRow dsRow = m_rowWriter.CreateDeclSecurityRow (
					secDec.Action,
					secDecls.Container.MetadataToken,
					m_mdWriter.AddBlob (secDec.Resolved ?
						m_mod.GetAsByteArray (secDec) : secDec.Blob));

				dsTable.Rows.Add (dsRow);
			}
		}

		public override void VisitCustomAttributeCollection (CustomAttributeCollection customAttrs)
		{
			if (customAttrs.Count == 0)
				return;

			CustomAttributeTable caTable = m_tableWriter.GetCustomAttributeTable ();
			foreach (CustomAttribute ca in customAttrs) {
				MetadataToken parent;
				if (customAttrs.Container is AssemblyDefinition)
					parent = new MetadataToken (TokenType.Assembly, 1);
				else if (customAttrs.Container is ModuleDefinition)
					parent = new MetadataToken (TokenType.Module, 1);
				else if (customAttrs.Container is IMetadataTokenProvider)
					parent = ((IMetadataTokenProvider) customAttrs.Container).MetadataToken;
				else
					throw new ReflectionException ("Unknown Custom Attribute parent");

				uint value = ca.Resolved ?
					m_sigWriter.AddCustomAttribute (GetCustomAttributeSig (ca), ca.Constructor) :
					m_mdWriter.AddBlob (m_mod.GetAsByteArray (ca));
				CustomAttributeRow caRow = m_rowWriter.CreateCustomAttributeRow (
					parent,
					GetMemberRefToken (ca.Constructor),
					value);

				caTable.Rows.Add (caRow);
			}
		}

		public override void VisitMarshalSpec (MarshalSpec marshalSpec)
		{
			FieldMarshalTable fmTable = m_tableWriter.GetFieldMarshalTable ();
			FieldMarshalRow fmRow = m_rowWriter.CreateFieldMarshalRow (
				marshalSpec.Container.MetadataToken,
				m_sigWriter.AddMarshalSig (GetMarshalSig (marshalSpec)));

			fmTable.Rows.Add (fmRow);
		}

		void WriteConstant (IHasConstant hc, TypeReference type)
		{
			ConstantTable cTable = m_tableWriter.GetConstantTable ();
			ElementType et;
			if (type is TypeDefinition && (type as TypeDefinition).IsEnum) {
				Type t = hc.Constant.GetType ();
				if (t.IsEnum)
					t = Enum.GetUnderlyingType (t);

				et = GetCorrespondingType (string.Concat (t.Namespace, '.', t.Name));
			} else
				et = GetCorrespondingType (type.FullName);

			if (et == ElementType.Object || et == ElementType.Type || et == ElementType.String)
				et = hc.Constant == null ?
					ElementType.Class :
					GetCorrespondingType (hc.Constant.GetType ().FullName);

			ConstantRow cRow = m_rowWriter.CreateConstantRow (
				et,
				hc.MetadataToken,
				m_mdWriter.AddBlob (EncodeConstant (et, hc.Constant)));

			cTable.Rows.Add (cRow);
		}

		void WriteLayout (FieldDefinition field)
		{
			FieldLayoutTable flTable = m_tableWriter.GetFieldLayoutTable ();
			FieldLayoutRow flRow = m_rowWriter.CreateFieldLayoutRow (
				field.Offset,
				GetRidFor (field));

			flTable.Rows.Add (flRow);
		}

		void WriteLayout (TypeDefinition type)
		{
			ClassLayoutTable clTable = m_tableWriter.GetClassLayoutTable ();
			ClassLayoutRow clRow = m_rowWriter.CreateClassLayoutRow (
				type.PackingSize,
				type.ClassSize,
				GetRidFor (type));

			clTable.Rows.Add (clRow);
		}

		void WriteSemantic (MethodSemanticsAttributes attrs,
			IMetadataTokenProvider member, MethodDefinition meth)
		{
			MethodSemanticsTable msTable = m_tableWriter.GetMethodSemanticsTable ();
			MethodSemanticsRow msRow = m_rowWriter.CreateMethodSemanticsRow (
				attrs,
				GetRidFor (meth),
				member.MetadataToken);

			msTable.Rows.Add (msRow);
		}

		void SortTables ()
		{
			TablesHeap th = m_mdWriter.GetMetadataRoot ().Streams.TablesHeap;
			th.Sorted = 0;

			if (th.HasTable (NestedClassTable.RId))
				m_tableWriter.GetNestedClassTable ().Rows.Sort (
					TableComparers.NestedClass.Instance);
			th.Sorted |= ((long) 1 << NestedClassTable.RId);

			if (th.HasTable (InterfaceImplTable.RId))
				m_tableWriter.GetInterfaceImplTable ().Rows.Sort (
					TableComparers.InterfaceImpl.Instance);
			th.Sorted |= ((long) 1 << InterfaceImplTable.RId);

			if (th.HasTable (ConstantTable.RId))
				m_tableWriter.GetConstantTable ().Rows.Sort (
					TableComparers.Constant.Instance);
			th.Sorted |= ((long) 1 << ConstantTable.RId);

			if (th.HasTable (MethodSemanticsTable.RId))
				m_tableWriter.GetMethodSemanticsTable ().Rows.Sort (
					TableComparers.MethodSem.Instance);
			th.Sorted |= ((long) 1 << MethodSemanticsTable.RId);

			if (th.HasTable (FieldMarshalTable.RId))
				m_tableWriter.GetFieldMarshalTable ().Rows.Sort (
					TableComparers.FieldMarshal.Instance);
			th.Sorted |= ((long) 1 << FieldMarshalTable.RId);

			if (th.HasTable (ClassLayoutTable.RId))
				m_tableWriter.GetClassLayoutTable ().Rows.Sort (
					TableComparers.TypeLayout.Instance);
			th.Sorted |= ((long) 1 << ClassLayoutTable.RId);

			if (th.HasTable (FieldLayoutTable.RId))
				m_tableWriter.GetFieldLayoutTable ().Rows.Sort (
					TableComparers.FieldLayout.Instance);
			th.Sorted |= ((long) 1 << FieldLayoutTable.RId);

			if (th.HasTable (ImplMapTable.RId))
				m_tableWriter.GetImplMapTable ().Rows.Sort (
					TableComparers.PInvoke.Instance);
			th.Sorted |= ((long) 1 << ImplMapTable.RId);

			if (th.HasTable (FieldRVATable.RId))
				m_tableWriter.GetFieldRVATable ().Rows.Sort (
					TableComparers.FieldRVA.Instance);
			th.Sorted |= ((long) 1 << FieldRVATable.RId);

			if (th.HasTable (MethodImplTable.RId))
				m_tableWriter.GetMethodImplTable ().Rows.Sort (
					TableComparers.Override.Instance);
			th.Sorted |= ((long) 1 << MethodImplTable.RId);

			if (th.HasTable (CustomAttributeTable.RId))
				m_tableWriter.GetCustomAttributeTable ().Rows.Sort (
					TableComparers.CustomAttribute.Instance);
			th.Sorted |= ((long) 1 << CustomAttributeTable.RId);

			if (th.HasTable (DeclSecurityTable.RId))
				m_tableWriter.GetDeclSecurityTable ().Rows.Sort (
					TableComparers.SecurityDeclaration.Instance);
			th.Sorted |= ((long) 1 << DeclSecurityTable.RId);
		}

		void CompleteGenericTables ()
		{
			if (m_genericParamStack.Count == 0)
				return;

			TablesHeap th = m_mdWriter.GetMetadataRoot ().Streams.TablesHeap;
			GenericParamTable gpTable = m_tableWriter.GetGenericParamTable ();

			m_genericParamStack.Sort (TableComparers.GenericParam.Instance);

			foreach (GenericParameter gp in m_genericParamStack) {
				GenericParamRow gpRow = m_rowWriter.CreateGenericParamRow (
					(ushort) gp.Owner.GenericParameters.IndexOf (gp),
					gp.Attributes,
					gp.Owner.MetadataToken,
					m_mdWriter.AddString (gp.Name));

				gpTable.Rows.Add (gpRow);
				gp.MetadataToken = new MetadataToken (TokenType.GenericParam, (uint) gpTable.Rows.Count);

				if (gp.HasCustomAttributes)
					VisitCustomAttributeCollection (gp.CustomAttributes);

				if (!gp.HasConstraints)
					continue;

				GenericParamConstraintTable gpcTable = m_tableWriter.GetGenericParamConstraintTable ();

				foreach (TypeReference constraint in gp.Constraints) {
					GenericParamConstraintRow gpcRow = m_rowWriter.CreateGenericParamConstraintRow (
						(uint) gpTable.Rows.Count,
						GetTypeDefOrRefToken (constraint));

					gpcTable.Rows.Add (gpcRow);
				}
			}

			th.Sorted |= ((long) 1 << GenericParamTable.RId);
			th.Sorted |= ((long) 1 << GenericParamConstraintTable.RId);
		}

		public override void TerminateModuleDefinition (ModuleDefinition module)
		{
			if (module.Assembly.HasCustomAttributes)
				VisitCustomAttributeCollection (module.Assembly.CustomAttributes);
			if (module.Assembly.HasSecurityDeclarations)
				VisitSecurityDeclarationCollection (module.Assembly.SecurityDeclarations);
			if (module.HasCustomAttributes)
				VisitCustomAttributeCollection (module.CustomAttributes);

			CompleteGenericTables ();
			SortTables ();

			MethodTable mTable = m_tableWriter.GetMethodTable ();
			for (int i = 0; i < m_methodStack.Count; i++) {
				MethodDefinition meth = (MethodDefinition) m_methodStack [i];
				if (meth.HasBody)
					mTable [i].RVA = m_codeWriter.WriteMethodBody (meth);
			}

			if (m_fieldStack.Count > 0) {
				FieldRVATable frTable = null;
				foreach (FieldDefinition field in m_fieldStack) {
					if (field.InitialValue != null && field.InitialValue.Length > 0) {
						if (frTable == null)
							frTable = m_tableWriter.GetFieldRVATable ();

						FieldRVARow frRow = m_rowWriter.CreateFieldRVARow (
							m_mdWriter.GetDataCursor (),
							field.MetadataToken.RID);

						m_mdWriter.AddData (field.InitialValue.Length + 3 & (~3));
						m_mdWriter.AddFieldInitData (field.InitialValue);

						frTable.Rows.Add (frRow);
					}
				}
			}

			if (m_symbolWriter != null)
				m_symbolWriter.Dispose ();

			if (m_mod.Assembly.EntryPoint != null)
				m_mdWriter.EntryPointToken =
					((uint) TokenType.Method) | GetRidFor (m_mod.Assembly.EntryPoint);

			m_mod.Image.MetadataRoot.Accept (m_mdWriter);
		}

		public static ElementType GetCorrespondingType (string fullName)
		{
			switch (fullName) {
			case Constants.Boolean :
				return ElementType.Boolean;
			case Constants.Char :
				return ElementType.Char;
			case Constants.SByte :
				return ElementType.I1;
			case Constants.Int16 :
				return ElementType.I2;
			case Constants.Int32 :
				return ElementType.I4;
			case Constants.Int64 :
				return ElementType.I8;
			case Constants.Byte :
				return ElementType.U1;
			case Constants.UInt16 :
				return ElementType.U2;
			case Constants.UInt32 :
				return ElementType.U4;
			case Constants.UInt64 :
				return ElementType.U8;
			case Constants.Single :
				return ElementType.R4;
			case Constants.Double :
				return ElementType.R8;
			case Constants.String :
				return ElementType.String;
			case Constants.Type :
				return ElementType.Type;
			case Constants.Object :
				return ElementType.Object;
			default:
				return ElementType.Class;
			}
		}

		byte [] EncodeConstant (ElementType et, object value)
		{
			m_constWriter.Empty ();

			if (value == null)
				et = ElementType.Class;

			IConvertible ic = value as IConvertible;
			IFormatProvider fp = CultureInfo.CurrentCulture.NumberFormat;

			switch (et) {
			case ElementType.Boolean :
				m_constWriter.Write ((byte) (ic.ToBoolean (fp) ? 1 : 0));
				break;
			case ElementType.Char :
				m_constWriter.Write ((ushort) ic.ToChar (fp));
				break;
			case ElementType.I1 :
				m_constWriter.Write (ic.ToSByte (fp));
				break;
			case ElementType.I2 :
				m_constWriter.Write (ic.ToInt16 (fp));
				break;
			case ElementType.I4 :
				m_constWriter.Write (ic.ToInt32 (fp));
				break;
			case ElementType.I8 :
				m_constWriter.Write (ic.ToInt64 (fp));
				break;
			case ElementType.U1 :
				m_constWriter.Write (ic.ToByte (fp));
				break;
			case ElementType.U2 :
				m_constWriter.Write (ic.ToUInt16 (fp));
				break;
			case ElementType.U4 :
				m_constWriter.Write (ic.ToUInt32 (fp));
				break;
			case ElementType.U8 :
				m_constWriter.Write (ic.ToUInt64 (fp));
				break;
			case ElementType.R4 :
				m_constWriter.Write (ic.ToSingle (fp));
				break;
			case ElementType.R8 :
				m_constWriter.Write (ic.ToDouble (fp));
				break;
			case ElementType.String :
				m_constWriter.Write (Encoding.Unicode.GetBytes ((string) value));
				break;
			case ElementType.Class :
				m_constWriter.Write (new byte [4]);
				break;
			default :
				throw new ArgumentException ("Non valid element for a constant");
			}

			return m_constWriter.ToArray ();
		}

		public SigType GetSigType (TypeReference type)
		{
			string name = type.FullName;

			switch (name) {
			case Constants.Void :
				return new SigType (ElementType.Void);
			case Constants.Object :
				return new SigType (ElementType.Object);
			case Constants.Boolean :
				return new SigType (ElementType.Boolean);
			case Constants.String :
				return new SigType (ElementType.String);
			case Constants.Char :
				return new SigType (ElementType.Char);
			case Constants.SByte :
				return new SigType (ElementType.I1);
			case Constants.Byte :
				return new SigType (ElementType.U1);
			case Constants.Int16 :
				return new SigType (ElementType.I2);
			case Constants.UInt16 :
				return new SigType (ElementType.U2);
			case Constants.Int32 :
				return new SigType (ElementType.I4);
			case Constants.UInt32 :
				return new SigType (ElementType.U4);
			case Constants.Int64 :
				return new SigType (ElementType.I8);
			case Constants.UInt64 :
				return new SigType (ElementType.U8);
			case Constants.Single :
				return new SigType (ElementType.R4);
			case Constants.Double :
				return new SigType (ElementType.R8);
			case Constants.IntPtr :
				return new SigType (ElementType.I);
			case Constants.UIntPtr :
				return new SigType (ElementType.U);
			case Constants.TypedReference :
				return new SigType (ElementType.TypedByRef);
			}

			if (type is GenericParameter) {
				GenericParameter gp = type as GenericParameter;
				int pos = gp.Owner.GenericParameters.IndexOf (gp);
				if (gp.Owner is TypeReference)
					return new VAR (pos);
				else if (gp.Owner is MethodReference)
					return new MVAR (pos);
				else
					throw new ReflectionException ("Unkown generic parameter type");
			} else if (type is GenericInstanceType) {
				GenericInstanceType git = type as GenericInstanceType;
				GENERICINST gi = new GENERICINST ();
				gi.ValueType = git.IsValueType;
				gi.Type = GetTypeDefOrRefToken (git.ElementType);
				gi.Signature = new GenericInstSignature ();
				gi.Signature.Arity = git.GenericArguments.Count;
				gi.Signature.Types = new GenericArg [gi.Signature.Arity];
				for (int i = 0; i < git.GenericArguments.Count; i++)
					gi.Signature.Types [i] = GetGenericArgSig (git.GenericArguments [i]);

				return gi;
			} else if (type is ArrayType) {
				ArrayType aryType = type as ArrayType;
				if (aryType.IsSizedArray) {
					SZARRAY szary = new SZARRAY ();
					szary.CustomMods = GetCustomMods (aryType.ElementType);
					szary.Type = GetSigType (aryType.ElementType);
					return szary;
				}

				// not optimized
				ArrayShape shape = new ArrayShape ();
				shape.Rank = aryType.Dimensions.Count;
				shape.NumSizes = 0;

				for (int i = 0; i < shape.Rank; i++) {
					ArrayDimension dim = aryType.Dimensions [i];
					if (dim.UpperBound > 0)
						shape.NumSizes++;
				}

				shape.Sizes = new int [shape.NumSizes];
				shape.NumLoBounds = shape.Rank;
				shape.LoBounds = new int [shape.NumLoBounds];

				for (int i = 0; i < shape.Rank; i++) {
					ArrayDimension dim = aryType.Dimensions [i];
					shape.LoBounds [i] = dim.LowerBound;
					if (dim.UpperBound > 0)
						shape.Sizes [i] = dim.UpperBound - dim.LowerBound + 1;
				}

				ARRAY ary = new ARRAY ();
				ary.Shape = shape;
				ary.CustomMods = GetCustomMods (aryType.ElementType);
				ary.Type = GetSigType (aryType.ElementType);
				return ary;
			} else if (type is PointerType) {
				PTR p = new PTR ();
				TypeReference elementType = (type as PointerType).ElementType;
				p.Void = elementType.FullName == Constants.Void;
				if (!p.Void) {
					p.CustomMods = GetCustomMods (elementType);
					p.PtrType = GetSigType (elementType);
				}
				return p;
			} else if (type is FunctionPointerType) {
				FNPTR fp = new FNPTR ();
				FunctionPointerType fptr = type as FunctionPointerType;

				int sentinel = fptr.GetSentinel ();
				if (sentinel < 0)
					fp.Method = GetMethodDefSig (fptr);
				else
					fp.Method = GetMethodRefSig (fptr);

				return fp;
			} else if (type is TypeSpecification) {
				return GetSigType ((type as TypeSpecification).ElementType);
			} else if (type.IsValueType) {
				VALUETYPE vt = new VALUETYPE ();
				vt.Type = GetTypeDefOrRefToken (type);
				return vt;
			} else {
				CLASS c = new CLASS ();
				c.Type = GetTypeDefOrRefToken (type);
				return c;
			}
		}

		public GenericArg GetGenericArgSig (TypeReference type)
		{
			GenericArg arg = new GenericArg (GetSigType (type));
			arg.CustomMods = GetCustomMods (type);
			return arg;
		}

		public CustomMod [] GetCustomMods (TypeReference type)
		{
			ModType modifier = type as ModType;
			if (modifier == null)
				return CustomMod.EmptyCustomMod;

			ArrayList cmods = new ArrayList ();
			do {
				CustomMod cmod = new CustomMod ();
				cmod.TypeDefOrRef = GetTypeDefOrRefToken (modifier.ModifierType);

				if (modifier is ModifierOptional)
					cmod.CMOD = CustomMod.CMODType.OPT;
				else if (modifier is ModifierRequired)
					cmod.CMOD = CustomMod.CMODType.REQD;

				cmods.Add (cmod);
				modifier = modifier.ElementType as ModType;
			} while (modifier != null);

			return cmods.ToArray (typeof (CustomMod)) as CustomMod [];
		}

		public Signature GetMemberRefSig (MemberReference member)
		{
			if (member is FieldReference)
				return GetFieldSig (member as FieldReference);
			else
				return GetMemberRefSig (member as MethodReference);
		}

		public FieldSig GetFieldSig (FieldReference field)
		{
			FieldSig sig = new FieldSig ();
			sig.CallingConvention |= 0x6;
			sig.Field = true;
			sig.CustomMods = GetCustomMods (field.FieldType);
			sig.Type = GetSigType (field.FieldType);
			return sig;
		}

		Param [] GetParametersSig (ParameterDefinitionCollection parameters)
		{
			Param [] ret = new Param [parameters.Count];
			for (int i = 0; i < ret.Length; i++) {
				ParameterDefinition pDef = parameters [i];
				Param p = new Param ();
				p.CustomMods = GetCustomMods (pDef.ParameterType);
				if (pDef.ParameterType.FullName == Constants.TypedReference)
					p.TypedByRef = true;
				else if (IsByReferenceType (pDef.ParameterType)) {
					p.ByRef = true;
					p.Type = GetSigType (pDef.ParameterType);
				} else
					p.Type = GetSigType (pDef.ParameterType);
				ret [i] = p;
			}
			return ret;
		}

		void CompleteMethodSig (IMethodSignature meth, MethodSig sig)
		{
			sig.HasThis = meth.HasThis;
			sig.ExplicitThis = meth.ExplicitThis;
			if (sig.HasThis)
				sig.CallingConvention |= 0x20;
			if (sig.ExplicitThis)
				sig.CallingConvention |= 0x40;

			if ((meth.CallingConvention & MethodCallingConvention.VarArg) != 0)
				sig.CallingConvention |= 0x5;

			sig.ParamCount = meth.Parameters.Count;
			sig.Parameters = GetParametersSig (meth.Parameters);

			RetType rtSig = new RetType ();
			rtSig.CustomMods = GetCustomMods (meth.ReturnType.ReturnType);

			if (meth.ReturnType.ReturnType.FullName == Constants.Void)
				rtSig.Void = true;
			else if (meth.ReturnType.ReturnType.FullName == Constants.TypedReference)
				rtSig.TypedByRef = true;
			else if (IsByReferenceType (meth.ReturnType.ReturnType)) {
				rtSig.ByRef = true;
				rtSig.Type = GetSigType (meth.ReturnType.ReturnType);
			} else
				rtSig.Type = GetSigType (meth.ReturnType.ReturnType);

			sig.RetType = rtSig;
		}

		static bool IsByReferenceType (TypeReference type)
		{
			TypeSpecification ts = type as TypeSpecification;
			while (ts != null) {
				if (ts is ReferenceType)
					return true;
				ts = ts.ElementType as TypeSpecification;
			}
			return false;
		}

		public MethodRefSig GetMethodRefSig (IMethodSignature meth)
		{
			MethodReference methodRef = meth as MethodReference;
			if (methodRef != null && methodRef.GenericParameters.Count > 0)
				return GetMethodDefSig (meth);

			MethodRefSig methSig = new MethodRefSig ();

			CompleteMethodSig (meth, methSig);

			int sentinel = meth.GetSentinel ();
			if (sentinel >= 0)
				methSig.Sentinel = sentinel;

			if ((meth.CallingConvention & MethodCallingConvention.C) != 0)
				methSig.CallingConvention |= 0x1;
			else if ((meth.CallingConvention & MethodCallingConvention.StdCall) != 0)
				methSig.CallingConvention |= 0x2;
			else if ((meth.CallingConvention & MethodCallingConvention.ThisCall) != 0)
				methSig.CallingConvention |= 0x3;
			else if ((meth.CallingConvention & MethodCallingConvention.FastCall) != 0)
				methSig.CallingConvention |= 0x4;

			return methSig;
		}

		public MethodDefSig GetMethodDefSig (IMethodSignature meth)
		{
			MethodDefSig sig = new MethodDefSig ();

			CompleteMethodSig (meth, sig);

			MethodReference methodRef = meth as MethodReference;
			if (methodRef != null && methodRef.GenericParameters.Count > 0) {
				sig.CallingConvention |= 0x10;
				sig.GenericParameterCount = methodRef.GenericParameters.Count;
			}

			return sig;
		}

		public PropertySig GetPropertySig (PropertyDefinition prop)
		{
			PropertySig ps = new PropertySig ();
			ps.CallingConvention |= 0x8;

			bool hasThis;
			bool explicitThis;
			MethodCallingConvention mcc;
			ParameterDefinitionCollection parameters = prop.Parameters;

			MethodDefinition meth;
			if (prop.GetMethod != null)
				meth = prop.GetMethod;
			else if (prop.SetMethod != null)
				meth = prop.SetMethod;
			else
				meth = null;

			if (meth != null) {
				hasThis = meth.HasThis;
				explicitThis = meth.ExplicitThis;
				mcc = meth.CallingConvention;
			} else {
				hasThis = explicitThis = false;
				mcc = MethodCallingConvention.Default;
			}

			if (hasThis)
				ps.CallingConvention |= 0x20;
			if (explicitThis)
				ps.CallingConvention |= 0x40;

			if ((mcc & MethodCallingConvention.VarArg) != 0)
				ps.CallingConvention |= 0x5;

			int paramCount = parameters != null ? parameters.Count : 0;

			ps.ParamCount = paramCount;
			ps.Parameters = GetParametersSig (parameters);
			ps.CustomMods = GetCustomMods (prop.PropertyType);
			ps.Type = GetSigType (prop.PropertyType);

			return ps;
		}

		public TypeSpec GetTypeSpecSig (TypeReference type)
		{
			TypeSpec ts = new TypeSpec ();
			ts.CustomMods = GetCustomMods (type);
			ts.Type = GetSigType (type);
			return ts;
		}

		public MethodSpec GetMethodSpecSig (GenericInstanceMethod gim)
		{
			GenericInstSignature gis = new GenericInstSignature ();
			gis.Arity = gim.GenericArguments.Count;
			gis.Types = new GenericArg [gis.Arity];
			for (int i = 0; i < gis.Arity; i++)
				gis.Types [i] = GetGenericArgSig (gim.GenericArguments [i]);

			return new MethodSpec (gis);
		}

		static string GetObjectTypeName (object o)
		{
			Type t = o.GetType ();
			return string.Concat (t.Namespace, ".", t.Name);
		}

		static CustomAttrib.Elem CreateElem (TypeReference type, object value)
		{
			CustomAttrib.Elem elem = new CustomAttrib.Elem ();
			elem.Value = value;
			elem.ElemType = type;
			elem.FieldOrPropType = GetCorrespondingType (type.FullName);

			switch (elem.FieldOrPropType) {
			case ElementType.Boolean :
			case ElementType.Char :
			case ElementType.R4 :
			case ElementType.R8 :
			case ElementType.I1 :
			case ElementType.I2 :
			case ElementType.I4 :
			case ElementType.I8 :
			case ElementType.U1 :
			case ElementType.U2 :
			case ElementType.U4 :
			case ElementType.U8 :
				elem.Simple = true;
				break;
			case ElementType.String:
				elem.String = true;
				break;
			case ElementType.Type:
				elem.Type = true;
				break;
			case ElementType.Object:
				elem.BoxedValueType = true;
				if (value == null)
					elem.FieldOrPropType = ElementType.String;
				else
					elem.FieldOrPropType = GetCorrespondingType (
						GetObjectTypeName (value));
				break;
			}

			return elem;
		}

		static CustomAttrib.FixedArg CreateFixedArg (TypeReference type, object value)
		{
			CustomAttrib.FixedArg fa = new CustomAttrib.FixedArg ();
			if (value is object []) {
				fa.SzArray = true;
				object [] values = value as object [];
				TypeReference obj = ((ArrayType) type).ElementType;
				fa.NumElem = (uint) values.Length;
				fa.Elems = new CustomAttrib.Elem [values.Length];
				for (int i = 0; i < values.Length; i++)
					fa.Elems [i] = CreateElem (obj, values [i]);
			} else {
				fa.Elems = new CustomAttrib.Elem [1];
				fa.Elems [0] = CreateElem (type, value);
			}

			return fa;
		}

		static CustomAttrib.NamedArg CreateNamedArg (TypeReference type, string name,
			object value, bool field)
		{
			CustomAttrib.NamedArg na = new CustomAttrib.NamedArg ();
			na.Field = field;
			na.Property = !field;

			na.FieldOrPropName = name;
			na.FieldOrPropType = GetCorrespondingType (type.FullName);
			na.FixedArg = CreateFixedArg (type, value);

			return na;
		}

		public static CustomAttrib GetCustomAttributeSig (CustomAttribute ca)
		{
			CustomAttrib cas = new CustomAttrib (ca.Constructor);
			cas.Prolog = CustomAttrib.StdProlog;

			cas.FixedArgs = new CustomAttrib.FixedArg [ca.Constructor.Parameters.Count];

			for (int i = 0; i < cas.FixedArgs.Length; i++)
				cas.FixedArgs [i] = CreateFixedArg (
					ca.Constructor.Parameters [i].ParameterType, ca.ConstructorParameters [i]);

			int nn = ca.Fields.Count + ca.Properties.Count;
			cas.NumNamed = (ushort) nn;
			cas.NamedArgs = new CustomAttrib.NamedArg [nn];

			if (cas.NamedArgs.Length > 0) {
				int curs = 0;
				foreach (DictionaryEntry entry in ca.Fields) {
					string field = (string) entry.Key;
					cas.NamedArgs [curs++] = CreateNamedArg (
						ca.GetFieldType (field), field, entry.Value, true);
				}

				foreach (DictionaryEntry entry in ca.Properties) {
					string property = (string) entry.Key;
					cas.NamedArgs [curs++] = CreateNamedArg (
						ca.GetPropertyType (property), property, entry.Value, false);
				}
			}

			return cas;
		}

		static MarshalSig GetMarshalSig (MarshalSpec mSpec)
		{
			MarshalSig ms = new MarshalSig (mSpec.NativeIntrinsic);

			if (mSpec is ArrayMarshalSpec) {
				ArrayMarshalSpec amd = mSpec as ArrayMarshalSpec;
				MarshalSig.Array ar = new MarshalSig.Array ();
				ar.ArrayElemType = amd.ElemType;
				ar.NumElem = amd.NumElem;
				ar.ParamNum = amd.ParamNum;
				ar.ElemMult = amd.ElemMult;
				ms.Spec = ar;
			} else if (mSpec is CustomMarshalerSpec) {
				CustomMarshalerSpec cmd = mSpec as CustomMarshalerSpec;
				MarshalSig.CustomMarshaler cm = new MarshalSig.CustomMarshaler ();
				cm.Guid = cmd.Guid.ToString ();
				cm.UnmanagedType = cmd.UnmanagedType;
				cm.ManagedType = cmd.ManagedType;
				cm.Cookie = cmd.Cookie;
				ms.Spec = cm;
			} else if (mSpec is FixedArraySpec) {
				FixedArraySpec fad = mSpec as FixedArraySpec;
				MarshalSig.FixedArray fa = new MarshalSig.FixedArray ();
				fa.ArrayElemType  = fad.ElemType;
				fa.NumElem = fad.NumElem;
				ms.Spec = fa;
			} else if (mSpec is FixedSysStringSpec) {
				MarshalSig.FixedSysString fss = new MarshalSig.FixedSysString ();
				fss.Size = (mSpec as FixedSysStringSpec).Size;
				ms.Spec = fss;
			} else if (mSpec is SafeArraySpec) {
				MarshalSig.SafeArray sa = new MarshalSig.SafeArray ();
				sa.ArrayElemType = (mSpec as SafeArraySpec).ElemType;
				ms.Spec = sa;
			}

			return ms;
		}

		public void WriteSymbols (ModuleDefinition module)
		{
			if (!m_saveSymbols)
				return;

			if (m_asmOutput == null)
				m_asmOutput = module.Assembly.Name.Name + "." + (module.Assembly.Kind == AssemblyKind.Dll ? "dll" : "exe");

			if (m_symbolWriter == null)
				m_symbolWriter = SymbolStoreHelper.GetWriter (module, m_asmOutput);

			foreach (TypeDefinition type in module.Types) {
				if (type.HasMethods) {
					foreach (MethodDefinition method in type.Methods)
						WriteSymbols (method);
				}
				if (type.HasConstructors) {
					foreach (MethodDefinition ctor in type.Constructors)
						WriteSymbols (ctor);
				}
			}

			m_symbolWriter.Dispose ();
		}

		void WriteSymbols (MethodDefinition meth)
		{
			if (!meth.HasBody)
				return;

			m_symbolWriter.Write (meth.Body);
		}
	}
}
