// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Reflection; namespace ICSharpCode.SharpDevelop.Dom { /// /// This class can write Dom entity into a binary file for fast loading. /// public sealed class DomPersistence { public const long FileMagic = 0x11635233ED2F428C; public const long IndexFileMagic = 0x11635233ED2F427D; public const short FileVersion = 28; 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) { // create cache directory, if necessary Directory.CreateDirectory(cacheDirectory); string assemblyFullName = pc.AssemblyFullName; int pos = assemblyFullName.IndexOf(','); string fileName = Path.Combine(cacheDirectory, assemblyFullName.Substring(0, pos) + "." + 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, FileShare.Read | FileShare.Delete, 4096, FileOptions.SequentialScan)) { return LoadProjectContent(fs); } } #endregion #region Cache index string GetIndexFileName() { return Path.Combine(cacheDirectory, "index.dat"); } Dictionary cacheIndex; Dictionary CacheIndex { get { return cacheIndex; } } Dictionary LoadCacheIndex() { string indexFile = GetIndexFileName(); Dictionary list = new Dictionary(StringComparer.OrdinalIgnoreCase); 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 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 e in cacheIndex) { writer.Write(e.Key); writer.Write(e.Value); } } } } void AddFileNameToCacheIndex(string cacheFile, ReflectionProjectContent pc) { Dictionary 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 /// /// Saves the project content to the stream. /// public static void WriteProjectContent(ReflectionProjectContent pc, Stream stream) { BinaryWriter writer = new BinaryWriter(stream); new ReadWriteHelper(writer).WriteProjectContent(pc); // do not close the stream } /// /// Load a project content from a stream. /// 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(); pc.AssemblyCompilationUnit.Freeze(); } 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 != myClassNameTypeCountPair.ClassName) return false; if (TypeParameterCount != myClassNameTypeCountPair.TypeParameterCount) return false; return true; } public override int GetHashCode() { return ClassName.GetHashCode() ^ ((int)TypeParameterCount * 5); } } private sealed class ReadWriteHelper { ReflectionProjectContent pc; // for writing: readonly BinaryWriter writer; readonly Dictionary classIndices = new Dictionary(); readonly Dictionary stringDict = new Dictionary(); // 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 classes = pc.Classes; IList assemblyAttributes = pc.GetAssemblyAttributes(); classIndices.Clear(); stringDict.Clear(); int i = 0; foreach (IClass c in classes) { classIndices[new ClassNameTypeCountPair(c)] = i; i += 1; } List externalTypes = new List(); List stringList = new List(); 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).CalculatedFlags); } 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 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.CalculatedFlags = 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 /// /// Finds all return types used in the class collection and adds the unknown ones /// to the externalTypeIndices and externalTypes collections. /// void CreateExternalTypeList(List externalTypes, List stringList, int classCount, ICollection 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 externalTypes, List 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 stringList, string text) { text = text ?? string.Empty; if (!stringDict.ContainsKey(text)) { stringDict.Add(text, stringList.Count); stringList.Add(text); } } void AddExternalType(IReturnType rt, List 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()) { AddExternalType(rt.CastToDecoratingReturnType().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; const int DynamicRTCode = -8; 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 if (name == "dynamic") { writer.Write(DynamicRTCode); } 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()) { writer.Write(PointerRTCode); WriteType(rt.CastToDecoratingReturnType().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()); case DynamicRTCode: return new DynamicReturnType(pc); 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 stringList, List externalTypes, int classCount, IList 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 pair in a.NamedArguments) { AddString(stringList, pair.Key); AddStringsAndExternalTypesFromAttributeArgument(stringList, attributes, externalTypes, classCount, pair.Value); } } } void WriteAttributes(IList 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 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 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 stringList, IList attributes, List 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 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 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 } } }