Click here to Skip to main content
15,895,746 members
Articles / Programming Languages / MSIL

Assembly Manipulation and C# / VB.NET Code Injection

Rate me:
Please Sign up or sign in to vote.
4.95/5 (200 votes)
26 Apr 2013MIT5 min read 927K   25K   486  
Reflexil is an assembly editor and runs as a plug-in for Reflector or JustDecompile. Reflexil is able to manipulate IL code and save the modified assemblies to disk. Reflexil also supports "on-the-fly" C#/VB.NET code injection.
// <file>
//     <copyright see="prj:///doc/copyright.txt"/>
//     <license see="prj:///doc/license.txt"/>
//     <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
//     <version>$Revision: 3527 $</version>
// </file>

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;

namespace ICSharpCode.SharpDevelop.Dom
{
	/// <summary>
	/// This class can write Dom entity into a binary file for fast loading.
	/// </summary>
	public sealed class DomPersistence
	{
		public const long FileMagic = 0x11635233ED2F428C;
		public const long IndexFileMagic = 0x11635233ED2F427D;
		public const short FileVersion = 16;
		
		ProjectContentRegistry registry;
		string cacheDirectory;
		
		internal string CacheDirectory {
			get {
				return cacheDirectory;
			}
		}
		
		internal DomPersistence(string cacheDirectory, ProjectContentRegistry registry)
		{
			this.cacheDirectory = cacheDirectory;
			this.registry = registry;
			
			cacheIndex = LoadCacheIndex();
		}
		
		#region Cache management
		public string SaveProjectContent(ReflectionProjectContent pc)
		{
			string assemblyFullName = pc.AssemblyFullName;
			int pos = assemblyFullName.IndexOf(',');
			string fileName = Path.Combine(cacheDirectory,
			                               assemblyFullName.Substring(0, pos)
			                               + "." + assemblyFullName.GetHashCode().ToString("x", CultureInfo.InvariantCulture)
			                               + "." + pc.AssemblyLocation.GetHashCode().ToString("x", CultureInfo.InvariantCulture)
			                               + ".dat");
			AddFileNameToCacheIndex(Path.GetFileName(fileName), pc);
			using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write)) {
				WriteProjectContent(pc, fs);
			}
			return fileName;
		}
		
		public ReflectionProjectContent LoadProjectContentByAssemblyName(string assemblyName)
		{
			string cacheFileName;
			if (CacheIndex.TryGetValue(assemblyName, out cacheFileName)) {
				cacheFileName = Path.Combine(cacheDirectory, cacheFileName);
				if (File.Exists(cacheFileName)) {
					return LoadProjectContent(cacheFileName);
				}
			}
			return null;
		}
		
		public ReflectionProjectContent LoadProjectContent(string cacheFileName)
		{
			using (FileStream fs = new FileStream(cacheFileName, FileMode.Open, FileAccess.Read)) {
				return LoadProjectContent(fs);
			}
		}
		#endregion
		
		#region Cache index
		string GetIndexFileName() { return Path.Combine(cacheDirectory, "index.dat"); }
		
		Dictionary<string, string> cacheIndex;
		
		Dictionary<string, string> CacheIndex {
			get {
				return cacheIndex;
			}
		}
		
		Dictionary<string, string> LoadCacheIndex()
		{
			string indexFile = GetIndexFileName();
			Dictionary<string, string> list = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
			if (File.Exists(indexFile)) {
				try {
					using (FileStream fs = new FileStream(indexFile, FileMode.Open, FileAccess.Read)) {
						using (BinaryReader reader = new BinaryReader(fs)) {
							if (reader.ReadInt64() != IndexFileMagic) {
								LoggingService.Warn("Index cache has wrong file magic");
								return list;
							}
							if (reader.ReadInt16() != FileVersion) {
								LoggingService.Warn("Index cache has wrong file version");
								return list;
							}
							int count = reader.ReadInt32();
							for (int i = 0; i < count; i++) {
								string key = reader.ReadString();
								list[key] = reader.ReadString();
							}
						}
					}
				} catch (IOException ex) {
					LoggingService.Warn("Error reading DomPersistance cache index", ex);
				}
			}
			return list;
		}
		
		void SaveCacheIndex(Dictionary<string, string> cacheIndex)
		{
			string indexFile = GetIndexFileName();
			using (FileStream fs = new FileStream(indexFile, FileMode.Create, FileAccess.Write)) {
				using (BinaryWriter writer = new BinaryWriter(fs)) {
					writer.Write(IndexFileMagic);
					writer.Write(FileVersion);
					writer.Write(cacheIndex.Count);
					foreach (KeyValuePair<string, string> e in cacheIndex) {
						writer.Write(e.Key);
						writer.Write(e.Value);
					}
				}
			}
		}
		
		void AddFileNameToCacheIndex(string cacheFile, ReflectionProjectContent pc)
		{
			Dictionary<string, string> l = LoadCacheIndex();
			l[pc.AssemblyLocation] = cacheFile;
			string txt = pc.AssemblyFullName;
			l[txt] = cacheFile;
			int pos = txt.LastIndexOf(',');
			do {
				txt = txt.Substring(0, pos);
				if (l.ContainsKey(txt))
					break;
				l[txt] = cacheFile;
				pos = txt.LastIndexOf(',');
			} while (pos >= 0);
			SaveCacheIndex(l);
			cacheIndex = l;
		}
		#endregion
		
		#region Saving / Loading without cache
		/// <summary>
		/// Saves the project content to the stream.
		/// </summary>
		public static void WriteProjectContent(ReflectionProjectContent pc, Stream stream)
		{
			BinaryWriter writer = new BinaryWriter(stream);
			new ReadWriteHelper(writer).WriteProjectContent(pc);
			// do not close the stream
		}
		
		/// <summary>
		/// Load a project content from a stream.
		/// </summary>
		public ReflectionProjectContent LoadProjectContent(Stream stream)
		{
			return LoadProjectContent(stream, registry);
		}
		
		public static ReflectionProjectContent LoadProjectContent(Stream stream, ProjectContentRegistry registry)
		{
			ReflectionProjectContent pc;
			BinaryReader reader = new BinaryReader(stream);
			try {
				pc = new ReadWriteHelper(reader).ReadProjectContent(registry);
				if (pc != null) {
					pc.InitializeSpecialClasses();
				}
				return pc;
			} catch (EndOfStreamException) {
				LoggingService.Warn("Read dom: EndOfStreamException");
				return null;
			} catch (Exception ex) {
				HostCallback.ShowMessage("Error loading cached code-completion data.\n" +
				                         "The cached file might be corrupted and will be regenerated.\n\n" +
				                         ex.ToString());
				return null;
			}
			// do not close the stream
		}
		#endregion
		
		private struct ClassNameTypeCountPair
		{
			public readonly string ClassName;
			public readonly byte TypeParameterCount;
			
			public ClassNameTypeCountPair(IClass c) {
				this.ClassName = c.FullyQualifiedName;
				this.TypeParameterCount = (byte)c.TypeParameters.Count;
			}
			
			public ClassNameTypeCountPair(IReturnType rt) {
				this.ClassName = rt.FullyQualifiedName;
				this.TypeParameterCount = (byte)rt.TypeArgumentCount;
			}
			
			public override bool Equals(object obj) {
				if (!(obj is ClassNameTypeCountPair)) return false;
				ClassNameTypeCountPair myClassNameTypeCountPair = (ClassNameTypeCountPair)obj;
				if (!ClassName.Equals(myClassNameTypeCountPair.ClassName, StringComparison.InvariantCultureIgnoreCase)) return false;
				if (TypeParameterCount != myClassNameTypeCountPair.TypeParameterCount) return false;
				return true;
			}
			
			public override int GetHashCode() {
				return StringComparer.InvariantCultureIgnoreCase.GetHashCode(ClassName) ^ ((int)TypeParameterCount * 5);
			}
		}
		
		private sealed class ReadWriteHelper
		{
			ReflectionProjectContent pc;
			
			// for writing:
			readonly BinaryWriter writer;
			readonly Dictionary<ClassNameTypeCountPair, int> classIndices = new Dictionary<ClassNameTypeCountPair, int>();
			readonly Dictionary<string, int> stringDict = new Dictionary<string, int>();
			
			// for reading:
			readonly BinaryReader reader;
			IReturnType[] types;
			string[] stringArray;
			
			#region Write/Read ProjectContent
			public ReadWriteHelper(BinaryWriter writer)
			{
				this.writer = writer;
			}
			
			public void WriteProjectContent(ReflectionProjectContent pc)
			{
				this.pc = pc;
				writer.Write(FileMagic);
				writer.Write(FileVersion);
				writer.Write(pc.AssemblyFullName);
				writer.Write(pc.AssemblyLocation);
				long time = 0;
				try {
					time = File.GetLastWriteTimeUtc(pc.AssemblyLocation).ToFileTime();
				} catch {}
				writer.Write(time);
				writer.Write(pc.ReferencedAssemblyNames.Count);
				foreach (DomAssemblyName name in pc.ReferencedAssemblyNames) {
					writer.Write(name.FullName);
				}
				WriteClasses();
			}
			
			public ReadWriteHelper(BinaryReader reader)
			{
				this.reader = reader;
			}
			
			public ReflectionProjectContent ReadProjectContent(ProjectContentRegistry registry)
			{
				if (reader.ReadInt64() != FileMagic) {
					LoggingService.Warn("Read dom: wrong magic");
					return null;
				}
				if (reader.ReadInt16() != FileVersion) {
					LoggingService.Warn("Read dom: wrong version");
					return null;
				}
				string assemblyName = reader.ReadString();
				string assemblyLocation = reader.ReadString();
				long time = 0;
				try {
					time = File.GetLastWriteTimeUtc(assemblyLocation).ToFileTime();
				} catch {}
				if (reader.ReadInt64() != time) {
					LoggingService.Warn("Read dom: assembly changed since cache was created");
					return null;
				}
				DomAssemblyName[] referencedAssemblies = new DomAssemblyName[reader.ReadInt32()];
				for (int i = 0; i < referencedAssemblies.Length; i++) {
					referencedAssemblies[i] = new DomAssemblyName(reader.ReadString());
				}
				this.pc = new ReflectionProjectContent(assemblyName, assemblyLocation, referencedAssemblies, registry);
				if (ReadClasses()) {
					return pc;
				} else {
					LoggingService.Warn("Read dom: error in file (invalid control mark)");
					return null;
				}
			}
			
			void WriteClasses()
			{
				ICollection<IClass> classes = pc.Classes;
				IList<IAttribute> assemblyAttributes = pc.GetAssemblyAttributes();
				
				classIndices.Clear();
				stringDict.Clear();
				int i = 0;
				foreach (IClass c in classes) {
					classIndices[new ClassNameTypeCountPair(c)] = i;
					i += 1;
				}
				
				List<ClassNameTypeCountPair> externalTypes = new List<ClassNameTypeCountPair>();
				List<string> stringList = new List<string>();
				CreateExternalTypeList(externalTypes, stringList, classes.Count, classes);
				AddStringsAndExternalTypesFromAttributes(stringList, externalTypes, classes.Count, assemblyAttributes);
				
				writer.Write(classes.Count);
				writer.Write(externalTypes.Count);
				foreach (IClass c in classes) {
					writer.Write(c.FullyQualifiedName);
				}
				foreach (ClassNameTypeCountPair type in externalTypes) {
					writer.Write(type.ClassName);
					writer.Write(type.TypeParameterCount);
				}
				writer.Write(stringList.Count);
				foreach (string text in stringList) {
					writer.Write(text);
				}
				WriteAttributes(assemblyAttributes);
				foreach (IClass c in classes) {
					WriteClass(c);
					// BinaryReader easily reads junk data when the file does not have the
					// expected format, so we put a checking byte after each class.
					writer.Write((byte)64);
				}
			}
			
			bool ReadClasses()
			{
				int classCount = reader.ReadInt32();
				int externalTypeCount = reader.ReadInt32();
				types = new IReturnType[classCount + externalTypeCount];
				DefaultClass[] classes = new DefaultClass[classCount];
				for (int i = 0; i < classes.Length; i++) {
					DefaultClass c = new DefaultClass(pc.AssemblyCompilationUnit, reader.ReadString());
					classes[i] = c;
					types[i] = c.DefaultReturnType;
				}
				for (int i = classCount; i < types.Length; i++) {
					string name = reader.ReadString();
					types[i] = new GetClassReturnType(pc, name, reader.ReadByte());
				}
				stringArray = new string[reader.ReadInt32()];
				for (int i = 0; i < stringArray.Length; i++) {
					stringArray[i] = reader.ReadString();
				}
				ReadAttributes(pc.AssemblyCompilationUnit);
				for (int i = 0; i < classes.Length; i++) {
					ReadClass(classes[i]);
					pc.AddClassToNamespaceList(classes[i]);
					if (reader.ReadByte() != 64) {
						return false;
					}
				}
				return true;
			}
			#endregion
			
			#region Write/Read Class
			IClass currentClass;
			
			void WriteClass(IClass c)
			{
				this.currentClass = c;
				WriteTemplates(c.TypeParameters);
				writer.Write(c.BaseTypes.Count);
				foreach (IReturnType type in c.BaseTypes) {
					WriteType(type);
				}
				writer.Write((int)c.Modifiers);
				if (c is DefaultClass) {
					writer.Write(((DefaultClass)c).Flags);
				} else {
					writer.Write((byte)0);
				}
				writer.Write((byte)c.ClassType);
				WriteAttributes(c.Attributes);
				writer.Write(c.InnerClasses.Count);
				foreach (IClass innerClass in c.InnerClasses) {
					writer.Write(innerClass.FullyQualifiedName);
					WriteClass(innerClass);
				}
				this.currentClass = c;
				writer.Write(c.Methods.Count);
				foreach (IMethod method in c.Methods) {
					WriteMethod(method);
				}
				writer.Write(c.Properties.Count);
				foreach (IProperty property in c.Properties) {
					WriteProperty(property);
				}
				writer.Write(c.Events.Count);
				foreach (IEvent evt in c.Events) {
					WriteEvent(evt);
				}
				writer.Write(c.Fields.Count);
				foreach (IField field in c.Fields) {
					WriteField(field);
				}
				this.currentClass = null;
			}
			
			void WriteTemplates(IList<ITypeParameter> list)
			{
				// read code exists twice: in ReadClass and ReadMethod
				writer.Write((byte)list.Count);
				foreach (ITypeParameter typeParameter in list) {
					WriteString(typeParameter.Name);
				}
				foreach (ITypeParameter typeParameter in list) {
					writer.Write(typeParameter.Constraints.Count);
					foreach (IReturnType type in typeParameter.Constraints) {
						WriteType(type);
					}
				}
			}
			
			void ReadClass(DefaultClass c)
			{
				this.currentClass = c;
				int count;
				count = reader.ReadByte();
				for (int i = 0; i < count; i++) {
					c.TypeParameters.Add(new DefaultTypeParameter(c, ReadString(), i));
				}
				if (count > 0) {
					foreach (ITypeParameter typeParameter in c.TypeParameters) {
						count = reader.ReadInt32();
						for (int i = 0; i < count; i++) {
							typeParameter.Constraints.Add(ReadType());
						}
					}
				} else {
					c.TypeParameters = DefaultTypeParameter.EmptyTypeParameterList;
				}
				count = reader.ReadInt32();
				for (int i = 0; i < count; i++) {
					c.BaseTypes.Add(ReadType());
				}
				c.Modifiers = (ModifierEnum)reader.ReadInt32();
				c.Flags = reader.ReadByte();
				c.ClassType = (ClassType)reader.ReadByte();
				ReadAttributes(c);
				count = reader.ReadInt32();
				for (int i = 0; i < count; i++) {
					DefaultClass innerClass = new DefaultClass(c.CompilationUnit, c);
					innerClass.FullyQualifiedName = reader.ReadString();
					c.InnerClasses.Add(innerClass);
					ReadClass(innerClass);
				}
				this.currentClass = c;
				count = reader.ReadInt32();
				for (int i = 0; i < count; i++) {
					c.Methods.Add(ReadMethod());
				}
				count = reader.ReadInt32();
				for (int i = 0; i < count; i++) {
					c.Properties.Add(ReadProperty());
				}
				count = reader.ReadInt32();
				for (int i = 0; i < count; i++) {
					c.Events.Add(ReadEvent());
				}
				count = reader.ReadInt32();
				for (int i = 0; i < count; i++) {
					c.Fields.Add(ReadField());
				}
				this.currentClass = null;
			}
			#endregion
			
			#region Write/Read return types / Collect strings
			/// <summary>
			/// Finds all return types used in the class collection and adds the unknown ones
			/// to the externalTypeIndices and externalTypes collections.
			/// </summary>
			void CreateExternalTypeList(List<ClassNameTypeCountPair> externalTypes,
			                            List<string> stringList,
			                            int classCount, ICollection<IClass> classes)
			{
				foreach (IClass c in classes) {
					CreateExternalTypeList(externalTypes, stringList, classCount, c.InnerClasses);
					AddStringsAndExternalTypesFromAttributes(stringList, externalTypes, classCount, c.Attributes);
					foreach (IReturnType returnType in c.BaseTypes) {
						AddExternalType(returnType, externalTypes, classCount);
					}
					foreach (ITypeParameter tp in c.TypeParameters) {
						AddString(stringList, tp.Name);
						foreach (IReturnType returnType in tp.Constraints) {
							AddExternalType(returnType, externalTypes, classCount);
						}
					}
					foreach (IField f in c.Fields) {
						CreateExternalTypeListMember(externalTypes, stringList, classCount, f);
					}
					foreach (IEvent f in c.Events) {
						CreateExternalTypeListMember(externalTypes, stringList, classCount, f);
					}
					foreach (IProperty p in c.Properties) {
						CreateExternalTypeListMember(externalTypes, stringList, classCount, p);
						foreach (IParameter parameter in p.Parameters) {
							AddString(stringList, parameter.Name);
							AddStringsAndExternalTypesFromAttributes(stringList, externalTypes, classCount, parameter.Attributes);
							AddExternalType(parameter.ReturnType, externalTypes, classCount);
						}
					}
					foreach (IMethod m in c.Methods) {
						CreateExternalTypeListMember(externalTypes, stringList, classCount, m);
						foreach (IParameter parameter in m.Parameters) {
							AddString(stringList, parameter.Name);
							AddStringsAndExternalTypesFromAttributes(stringList, externalTypes, classCount, parameter.Attributes);
							AddExternalType(parameter.ReturnType, externalTypes, classCount);
						}
						foreach (ITypeParameter tp in m.TypeParameters) {
							AddString(stringList, tp.Name);
							foreach (IReturnType returnType in tp.Constraints) {
								AddExternalType(returnType, externalTypes, classCount);
							}
						}
					}
				}
			}
			
			void CreateExternalTypeListMember(List<ClassNameTypeCountPair> externalTypes,
			                                  List<string> stringList, int classCount,
			                                  IMember member)
			{
				AddString(stringList, member.Name);
				AddStringsAndExternalTypesFromAttributes(stringList, externalTypes, classCount, member.Attributes);
				foreach (ExplicitInterfaceImplementation eii in member.InterfaceImplementations) {
					AddString(stringList, eii.MemberName);
					AddExternalType(eii.InterfaceReference, externalTypes, classCount);
				}
				AddExternalType(member.ReturnType, externalTypes, classCount);
			}
			
			void AddString(List<string> stringList, string text)
			{
				text = text ?? string.Empty;
				if (!stringDict.ContainsKey(text)) {
					stringDict.Add(text, stringList.Count);
					stringList.Add(text);
				}
			}
			
			void AddExternalType(IReturnType rt, List<ClassNameTypeCountPair> externalTypes, int classCount)
			{
				if (rt.IsDefaultReturnType) {
					ClassNameTypeCountPair pair = new ClassNameTypeCountPair(rt);
					if (!classIndices.ContainsKey(pair)) {
						classIndices.Add(pair, externalTypes.Count + classCount);
						externalTypes.Add(pair);
					}
				} else if (rt.IsGenericReturnType) {
					// ignore
				} else if (rt.IsArrayReturnType) {
					AddExternalType(rt.CastToArrayReturnType().ArrayElementType, externalTypes, classCount);
				} else if (rt.IsConstructedReturnType) {
					AddExternalType(rt.CastToConstructedReturnType().UnboundType, externalTypes, classCount);
					foreach (IReturnType typeArgument in rt.CastToConstructedReturnType().TypeArguments) {
						AddExternalType(typeArgument, externalTypes, classCount);
					}
				} else if (rt.IsDecoratingReturnType<PointerReturnType>()) {
					AddExternalType(rt.CastToDecoratingReturnType<PointerReturnType>().BaseType, externalTypes, classCount);
				} else {
					LoggingService.Warn("Unknown return type: " + rt.ToString());
				}
			}
			
			const int ArrayRTCode         = -1;
			const int ConstructedRTCode   = -2;
			const int TypeGenericRTCode   = -3;
			const int MethodGenericRTCode = -4;
			const int NullRTReferenceCode = -5;
			const int VoidRTCode          = -6;
			const int PointerRTCode       = -7;
			
			void WriteType(IReturnType rt)
			{
				if (rt == null) {
					writer.Write(NullRTReferenceCode);
					return;
				}
				if (rt.IsDefaultReturnType) {
					string name = rt.FullyQualifiedName;
					if (name == "System.Void") {
						writer.Write(VoidRTCode);
					} else {
						writer.Write(classIndices[new ClassNameTypeCountPair(rt)]);
					}
				} else if (rt.IsGenericReturnType) {
					GenericReturnType grt = rt.CastToGenericReturnType();
					if (grt.TypeParameter.Method != null) {
						writer.Write(MethodGenericRTCode);
					} else {
						writer.Write(TypeGenericRTCode);
					}
					writer.Write(grt.TypeParameter.Index);
				} else if (rt.IsArrayReturnType) {
					writer.Write(ArrayRTCode);
					writer.Write(rt.CastToArrayReturnType().ArrayDimensions);
					WriteType(rt.CastToArrayReturnType().ArrayElementType);
				} else if (rt.IsConstructedReturnType) {
					ConstructedReturnType crt = rt.CastToConstructedReturnType();
					writer.Write(ConstructedRTCode);
					WriteType(crt.UnboundType);
					writer.Write((byte)crt.TypeArguments.Count);
					foreach (IReturnType typeArgument in crt.TypeArguments) {
						WriteType(typeArgument);
					}
				} else if (rt.IsDecoratingReturnType<PointerReturnType>()) {
					writer.Write(PointerRTCode);
					WriteType(rt.CastToDecoratingReturnType<PointerReturnType>().BaseType);
				} else {
					writer.Write(NullRTReferenceCode);
					LoggingService.Warn("Unknown return type: " + rt.ToString());
				}
			}
			
			// outerClass and outerMethod are required for generic return types
			IReturnType ReadType()
			{
				int index = reader.ReadInt32();
				switch (index) {
					case ArrayRTCode:
						int dimensions = reader.ReadInt32();
						return new ArrayReturnType(pc, ReadType(), dimensions);
					case ConstructedRTCode:
						IReturnType baseType = ReadType();
						IReturnType[] typeArguments = new IReturnType[reader.ReadByte()];
						for (int i = 0; i < typeArguments.Length; i++) {
							typeArguments[i] = ReadType();
						}
						return new ConstructedReturnType(baseType, typeArguments);
					case TypeGenericRTCode:
						return new GenericReturnType(currentClass.TypeParameters[reader.ReadInt32()]);
					case MethodGenericRTCode:
						return new GenericReturnType(currentMethod.TypeParameters[reader.ReadInt32()]);
					case NullRTReferenceCode:
						return null;
					case VoidRTCode:
						return new VoidReturnType(pc);
					case PointerRTCode:
						return new PointerReturnType(ReadType());
					default:
						return types[index];
				}
			}
			#endregion
			
			#region Write/Read class member
			void WriteString(string text)
			{
				text = text ?? string.Empty;
				writer.Write(stringDict[text]);
			}
			
			string ReadString()
			{
				return stringArray[reader.ReadInt32()];
			}
			
			void WriteMember(IMember m)
			{
				WriteString(m.Name);
				writer.Write((int)m.Modifiers);
				WriteAttributes(m.Attributes);
				writer.Write((ushort)m.InterfaceImplementations.Count);
				foreach (ExplicitInterfaceImplementation iee in m.InterfaceImplementations) {
					WriteType(iee.InterfaceReference);
					WriteString(iee.MemberName);
				}
				if (!(m is IMethod)) {
					// method must store ReturnType AFTER Template definitions
					WriteType(m.ReturnType);
				}
			}
			
			void ReadMember(AbstractMember m)
			{
				// name is already read by the method that calls the member constructor
				m.Modifiers = (ModifierEnum)reader.ReadInt32();
				ReadAttributes(m);
				int interfaceImplCount = reader.ReadUInt16();
				for (int i = 0; i < interfaceImplCount; i++) {
					m.InterfaceImplementations.Add(new ExplicitInterfaceImplementation(ReadType(), ReadString()));
				}
				if (!(m is IMethod)) {
					m.ReturnType = ReadType();
				}
			}
			#endregion
			
			#region Write/Read attributes
			void AddStringsAndExternalTypesFromAttributes(List<string> stringList,
			                                              List<ClassNameTypeCountPair> externalTypes, int classCount,
			                                              IList<IAttribute> attributes)
			{
				foreach (IAttribute a in attributes) {
					AddExternalType(a.AttributeType, externalTypes, classCount);
					foreach (object o in a.PositionalArguments) {
						AddStringsAndExternalTypesFromAttributeArgument(stringList, attributes, externalTypes, classCount, o);
					}
					foreach (KeyValuePair<string, object> pair in a.NamedArguments) {
						AddString(stringList, pair.Key);
						AddStringsAndExternalTypesFromAttributeArgument(stringList, attributes, externalTypes, classCount, pair.Value);
					}
				}
			}
			
			void WriteAttributes(IList<IAttribute> attributes)
			{
				writer.Write((ushort)attributes.Count);
				foreach (IAttribute a in attributes) {
					WriteType(a.AttributeType);
					writer.Write((byte)a.AttributeTarget);
					writer.Write((byte)a.PositionalArguments.Count);
					foreach (object o in a.PositionalArguments) {
						WriteAttributeArgument(o);
					}
					writer.Write((byte)a.NamedArguments.Count);
					foreach (KeyValuePair<string, object> pair in a.NamedArguments) {
						WriteString(pair.Key);
						WriteAttributeArgument(pair.Value);
					}
				}
			}
			
			void ReadAttributes(ICompilationUnit cu)
			{
				int count = reader.ReadUInt16();
				ReadAttributes(cu.Attributes, count);
			}
			
			void ReadAttributes(DefaultParameter parameter)
			{
				int count = reader.ReadUInt16();
				if (count > 0) {
					ReadAttributes(parameter.Attributes, count);
				} else {
					parameter.Attributes = DefaultAttribute.EmptyAttributeList;
				}
			}
			
			void ReadAttributes(AbstractEntity decoration)
			{
				int count = reader.ReadUInt16();
				if (count > 0) {
					ReadAttributes(decoration.Attributes, count);
				} else {
					decoration.Attributes = DefaultAttribute.EmptyAttributeList;
				}
			}
			
			void ReadAttributes(IList<IAttribute> attributes, int count)
			{
				for (int i = 0; i < count; i++) {
					IReturnType type = ReadType();
					DefaultAttribute attr = new DefaultAttribute(type, (AttributeTarget)reader.ReadByte());
					int posArgCount = reader.ReadByte();
					for (int j = 0; j < posArgCount; j++) {
						attr.PositionalArguments.Add(ReadAttributeArgument());
					}
					int namedArgCount = reader.ReadByte();
					for (int j = 0; j < namedArgCount; j++) {
						attr.NamedArguments.Add(ReadString(), ReadAttributeArgument());
					}
					attributes.Add(attr);
				}
			}
			#endregion
			
			#region Write/Read attribute arguments
			void AddStringsAndExternalTypesFromAttributeArgument(List<string> stringList, IList<IAttribute> attributes,
			                                                     List<ClassNameTypeCountPair> externalTypes, int classCount,
			                                                     object value)
			{
				if (value is string) {
					AddString(stringList, (string)value);
				} else if (value is IReturnType) {
					AddExternalType((IReturnType)value, externalTypes, classCount);
				}
			}
			
			enum AttributeType : byte
			{
				Null,
				String,
				Type,
				SByte,
				Int16,
				Int32,
				Int64,
				Byte,
				UInt16,
				UInt32,
				UInt64,
				Bool,
				Single,
				Double,
			}
			
			void WriteAttributeArgument(object o)
			{
				if (o == null) {
					writer.Write((byte)AttributeType.Null);
				} else if (o is string) {
					writer.Write((byte)AttributeType.String);
					WriteString((string)o);
				} else if (o is IReturnType) {
					writer.Write((byte)AttributeType.Type);
					WriteType((IReturnType)o);
				} else if (o is Byte) {
					writer.Write((byte)AttributeType.Byte);
					writer.Write((Byte)o);
				} else if (o is Int16) {
					writer.Write((byte)AttributeType.Int16);
					writer.Write((Int16)o);
				} else if (o is Int32) {
					writer.Write((byte)AttributeType.Int32);
					writer.Write((Int32)o);
				} else if (o is Int64) {
					writer.Write((byte)AttributeType.Int64);
					writer.Write((Int64)o);
				} else if (o is SByte) {
					writer.Write((byte)AttributeType.SByte);
					writer.Write((SByte)o);
				} else if (o is UInt16) {
					writer.Write((byte)AttributeType.UInt16);
					writer.Write((UInt16)o);
				} else if (o is UInt32) {
					writer.Write((byte)AttributeType.UInt32);
					writer.Write((UInt32)o);
				} else if (o is UInt64) {
					writer.Write((byte)AttributeType.UInt64);
					writer.Write((UInt64)o);
				} else if (o is bool) {
					writer.Write((byte)AttributeType.Bool);
					writer.Write((bool)o);
				} else if (o is Single) {
					writer.Write((byte)AttributeType.Single);
					writer.Write((Single)o);
				} else if (o is Double) {
					writer.Write((byte)AttributeType.Double);
					writer.Write((Double)o);
				} else {
					writer.Write((byte)AttributeType.Null);
					LoggingService.Warn("Cannot write attribute arguments of type " + o.GetType());
				}
			}
			
			object ReadAttributeArgument()
			{
				byte type = reader.ReadByte();
				switch ((AttributeType)type) {
					case AttributeType.Null:
						return null;
					case AttributeType.String:
						return ReadString();
					case AttributeType.Type:
						return ReadType();
					case AttributeType.SByte:
						return reader.ReadSByte();
					case AttributeType.Int16:
						return reader.ReadInt16();
					case AttributeType.Int32:
						return reader.ReadInt32();
					case AttributeType.Int64:
						return reader.ReadInt64();
					case AttributeType.Byte:
						return reader.ReadByte();
					case AttributeType.UInt16:
						return reader.ReadUInt16();
					case AttributeType.UInt32:
						return reader.ReadUInt32();
					case AttributeType.UInt64:
						return reader.ReadUInt64();
					case AttributeType.Bool:
						return reader.ReadBoolean();
					case AttributeType.Single:
						return reader.ReadSingle();
					case AttributeType.Double:
						return reader.ReadDouble();
					default:
						throw new NotSupportedException("Invalid attribute argument type code " + type);
				}
			}
			#endregion
			
			#region Write/Read parameters
			void WriteParameters(IList<IParameter> parameters)
			{
				writer.Write((ushort)parameters.Count);
				foreach (IParameter p in parameters) {
					WriteString(p.Name);
					WriteType(p.ReturnType);
					writer.Write((byte)p.Modifiers);
					WriteAttributes(p.Attributes);
				}
			}
			
			void ReadParameters(DefaultMethod m)
			{
				int count = reader.ReadUInt16();
				if (count > 0) {
					ReadParameters(m.Parameters, count);
				} else {
					m.Parameters = DefaultParameter.EmptyParameterList;
				}
			}
			
			void ReadParameters(DefaultProperty m)
			{
				int count = reader.ReadUInt16();
				if (count > 0) {
					ReadParameters(m.Parameters, count);
				} else {
					m.Parameters = DefaultParameter.EmptyParameterList;
				}
			}
			
			void ReadParameters(IList<IParameter> parameters, int count)
			{
				for (int i = 0; i < count; i++) {
					string name = ReadString();
					DefaultParameter p = new DefaultParameter(name, ReadType(), DomRegion.Empty);
					p.Modifiers = (ParameterModifiers)reader.ReadByte();
					ReadAttributes(p);
					parameters.Add(p);
				}
			}
			#endregion
			
			#region Write/Read Method
			IMethod currentMethod;
			
			void WriteMethod(IMethod m)
			{
				currentMethod = m;
				WriteMember(m);
				WriteTemplates(m.TypeParameters);
				WriteType(m.ReturnType);
				writer.Write(m.IsExtensionMethod);
				WriteParameters(m.Parameters);
				currentMethod = null;
			}
			
			IMethod ReadMethod()
			{
				DefaultMethod m = new DefaultMethod(currentClass, ReadString());
				currentMethod = m;
				ReadMember(m);
				int count = reader.ReadByte();
				for (int i = 0; i < count; i++) {
					m.TypeParameters.Add(new DefaultTypeParameter(m, ReadString(), i));
				}
				if (count > 0) {
					foreach (ITypeParameter typeParameter in m.TypeParameters) {
						count = reader.ReadInt32();
						for (int i = 0; i < count; i++) {
							typeParameter.Constraints.Add(ReadType());
						}
					}
				} else {
					m.TypeParameters = DefaultTypeParameter.EmptyTypeParameterList;
				}
				m.ReturnType = ReadType();
				m.IsExtensionMethod = reader.ReadBoolean();
				ReadParameters(m);
				currentMethod = null;
				return m;
			}
			#endregion
			
			#region Write/Read Property
			void WriteProperty(IProperty p)
			{
				WriteMember(p);
				DefaultProperty dp = p as DefaultProperty;
				if (dp != null) {
					writer.Write(dp.accessFlags);
				} else {
					writer.Write((byte)0);
				}
				writer.Write((byte)((p.GetterModifiers != ModifierEnum.None ? 1 : 0) + (p.SetterModifiers != ModifierEnum.None ? 2 : 0)));
				if (p.GetterModifiers != ModifierEnum.None) {
					writer.Write((int)p.GetterModifiers);
				}
				if (p.SetterModifiers != ModifierEnum.None) {
					writer.Write((int)p.SetterModifiers);
				}
				WriteParameters(p.Parameters);
			}
			
			IProperty ReadProperty()
			{
				DefaultProperty p = new DefaultProperty(currentClass, ReadString());
				ReadMember(p);
				p.accessFlags = reader.ReadByte();
				byte b = reader.ReadByte();
				if ((b & 1) == 1) {
					p.GetterModifiers = (ModifierEnum)reader.ReadInt32();
				}
				if ((b & 2) == 2) {
					p.SetterModifiers = (ModifierEnum)reader.ReadInt32();
				}
				ReadParameters(p);
				return p;
			}
			#endregion
			
			#region Write/Read Event
			void WriteEvent(IEvent p)
			{
				WriteMember(p);
			}
			
			IEvent ReadEvent()
			{
				DefaultEvent p = new DefaultEvent(currentClass, ReadString());
				ReadMember(p);
				return p;
			}
			#endregion
			
			#region Write/Read Field
			void WriteField(IField p)
			{
				WriteMember(p);
			}
			
			IField ReadField()
			{
				DefaultField p = new DefaultField(currentClass, ReadString());
				ReadMember(p);
				return p;
			}
			#endregion
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer (Senior) Microsoft
United States United States
Sebastien Lebreton is a Software Engineer at Microsoft.

He is particularly interested in optimization, reverse engineering and distributed objects technologies.

Comments and Discussions