src/Shared/INodePacketTranslator.cs (71 lines of code) (raw):

// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. //----------------------------------------------------------------------- // </copyright> // <summary>Interface for objects which can Translate data for inter-node communication.</summary> //----------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Globalization; using System.IO; using Microsoft.Build.Framework; namespace Microsoft.Build.BackEnd { /// <summary> /// This delegate is used for objects which do not have public parameterless constructors and must be constructed using /// another method. When invoked, this delegate should return a new object which has been translated appropriately. /// </summary> /// <typeparam name="T">The type to be translated.</typeparam> internal delegate T NodePacketValueFactory<T>(INodePacketTranslator translator); /// <summary> /// This delegate is used to create arbitrary dictionary types for serialization. /// </summary> /// <typeparam name="T">The type of dictionary to be created.</typeparam> internal delegate T NodePacketDictionaryCreator<T>(int capacity); /// <summary> /// The serialization mode. /// </summary> internal enum TranslationDirection { /// <summary> /// Indicates the serializer is operating in write mode. /// </summary> WriteToStream, /// <summary> /// Indicates the serializer is operating in read mode. /// </summary> ReadFromStream } /// <summary> /// This interface represents an object which aids objects in serializing and /// deserializing INodePackets. /// </summary> /// <remarks> /// The reason we bother with a custom serialization mechanism at all is two fold: /// 1. The .Net serialization mechanism is inefficient, even if you implement ISerializable /// with your own custom mechanism. This is because the serializer uses a bag called /// SerializationInfo into which you are expected to drop all your data. This adds /// an unnecessary level of indirection to the serialization routines and prevents direct, /// efficient access to the byte-stream. /// 2. You have to implement both a reader and writer part, which introduces the potential for /// error should the classes be later modified. If the reader and writer methods are not /// kept in perfect sync, serialization errors will occur. Our custom serializer eliminates /// that by ensuring a single Translate method on a given object can handle both reads and /// writes without referencing any field more than once. /// </remarks> internal interface INodePacketTranslator { /// <summary> /// Returns the current serialization mode. /// </summary> TranslationDirection Mode { get; } /// <summary> /// Returns the binary reader. /// </summary> /// <remarks> /// This should ONLY be used when absolutely necessary for translation. It is generally unnecessary for the /// translating object to know the direction of translation. Use one of the Translate methods instead. /// </remarks> BinaryReader Reader { get; } /// <summary> /// Returns the binary writer. /// </summary> /// <remarks> /// This should ONLY be used when absolutely necessary for translation. It is generally unnecessary for the /// translating object to know the direction of translation. Use one of the Translate methods instead. /// </remarks> BinaryWriter Writer { get; } /// <summary> /// Translates a boolean. /// </summary> /// <param name="value">The value to be translated.</param> void Translate(ref bool value); /// <summary> /// Translates a byte. /// </summary> /// <param name="value">The value to be translated.</param> void Translate(ref byte value); /// <summary> /// Translates a short. /// </summary> /// <param name="value">The value to be translated.</param> void Translate(ref short value); /// <summary> /// Translates a unsigned short. /// </summary> /// <param name="value">The value to be translated.</param> void Translate(ref ushort value); /// <summary> /// Translates an integer. /// </summary> /// <param name="value">The value to be translated.</param> void Translate(ref int value); /// <summary> /// Translates a string. /// </summary> /// <param name="value">The value to be translated.</param> void Translate(ref string value); /// <summary> /// Translates a string array. /// </summary> /// <param name="array">The array to be translated.</param> void Translate(ref string[] array); /// <summary> /// Translates a list of strings /// </summary> /// <param name="list">The list to be translated.</param> void Translate(ref List<string> list); /// <summary> /// Translates a list of T where T implements INodePacketTranslateable /// </summary> /// <param name="list">The list to be translated.</param> /// <param name="factory">factory to create type T</param> /// <typeparam name="T">A TaskItemType</typeparam> void Translate<T>(ref List<T> list, NodePacketValueFactory<T> factory) where T : INodePacketTranslatable; /// <summary> /// Translates a DateTime. /// </summary> /// <param name="value">The value to be translated.</param> void Translate(ref DateTime value); // MSBuildTaskHost is based on CLR 3.5, which does not have the 6-parameter constructor for BuildEventContext, // which is what current implementations of this method use. However, it also does not ever need to translate // BuildEventContexts, so it should be perfectly safe to compile this method out of that assembly. I am compiling // the method out of the interface as well, instead of just making the method empty, so that if we ever do need // to translate BuildEventContexts from the CLR 3.5 task host, it will become immediately obvious, rather than // failing or misbehaving silently. #if !CLR2COMPATIBILITY /// <summary> /// Translates a BuildEventContext /// </summary> /// <remarks> /// This method exists only because there is no serialization method built into the BuildEventContext /// class, and it lives in Framework and we don't want to add a public method to it. /// </remarks> /// <param name="value">The context to be translated.</param> void Translate(ref BuildEventContext value); #endif /// <summary> /// Translates an enumeration. /// </summary> /// <typeparam name="T">The enumeration type.</typeparam> /// <param name="value">The enumeration instance to be translated.</param> /// <param name="numericValue">The enumeration value as an integer.</param> /// <remarks>This is a bit ugly, but it doesn't seem like a nice method signature is possible because /// you can't pass the enum type as a reference and constrain the generic parameter to Enum. Nor /// can you simply pass as ref Enum, because an enum instance doesn't match that function signature. /// Finally, converting the enum to an int assumes that we always want to transport enums as ints. This /// works in all of our current cases, but certainly isn't perfectly generic.</remarks> void TranslateEnum<T>(ref T value, int numericValue); #if FEATURE_BINARY_SERIALIZATION /// <summary> /// Translates a value using the .Net binary formatter. /// </summary> /// <typeparam name="T">The reference type.</typeparam> /// <param name="value">The value to be translated.</param> /// <remarks> /// The primary purpose of this method is to support serialization of Exceptions and /// custom build logging events, since these do not support our custom serialization /// methods. /// </remarks> void TranslateDotNet<T>(ref T value); #else // BuildEventArgs can't implement INodePacketTranslatable because it's in Microsoft.Build.Framework, which doesn't have that interface void TranslateBuildEventArgs(ref BuildEventArgs value); #endif void TranslateException(ref Exception value); /// <summary> /// Translates an object implementing INodePacketTranslatable. /// </summary> /// <typeparam name="T">The reference type.</typeparam> /// <param name="value">The value to be translated.</param> void Translate<T>(ref T value) where T : INodePacketTranslatable, new(); /// <summary> /// Translates an object implementing INodePacketTranslatable which does not expose a /// public parameterless constructor. /// </summary> /// <typeparam name="T">The reference type.</typeparam> /// <param name="value">The value to be translated.</param> /// <param name="factory">The factory method used to instantiate values of type T.</param> void Translate<T>(ref T value, NodePacketValueFactory<T> factory) where T : INodePacketTranslatable; /// <summary> /// Translates a culture /// </summary> /// <param name="culture">The culture</param> void TranslateCulture(ref CultureInfo culture); /// <summary> /// Translates a byte array /// </summary> /// <param name="byteArray">The array to be translated.</param> void Translate(ref byte[] byteArray); /// <summary> /// Translates an array of objects implementing INodePacketTranslatable. /// </summary> /// <typeparam name="T">The reference type.</typeparam> /// <param name="array">The array to be translated.</param> void TranslateArray<T>(ref T[] array) where T : INodePacketTranslatable, new(); /// <summary> /// Translates an array of objects implementing INodePacketTranslatable requiring a factory to create. /// </summary> /// <typeparam name="T">The reference type.</typeparam> /// <param name="array">The array to be translated.</param> /// <param name="factory">The factory method used to instantiate values of type T.</param> void TranslateArray<T>(ref T[] array, NodePacketValueFactory<T> factory) where T : INodePacketTranslatable; /// <summary> /// Translates a dictionary of { string, string }. /// </summary> /// <param name="dictionary">The dictionary to be translated.</param> /// <param name="comparer">The comparer used to instantiate the dictionary.</param> void TranslateDictionary(ref Dictionary<string, string> dictionary, IEqualityComparer<string> comparer); /// <summary> /// Translates a dictionary of { string, T }. /// </summary> /// <typeparam name="T">The reference type for the values, which implements INodePacketTranslatable.</typeparam> /// <param name="dictionary">The dictionary to be translated.</param> /// <param name="comparer">The comparer used to instantiate the dictionary.</param> /// <param name="valueFactory">The factory used to instantiate values in the dictionary.</param> void TranslateDictionary<T>(ref Dictionary<string, T> dictionary, IEqualityComparer<string> comparer, NodePacketValueFactory<T> valueFactory) where T : class, INodePacketTranslatable; /// <summary> /// Translates a dictionary of { string, T } for dictionaries with public parameterless constructors. /// </summary> /// <typeparam name="D">The reference type for the dictionary.</typeparam> /// <typeparam name="T">The reference type for values in the dictionary.</typeparam> /// <param name="dictionary">The dictionary to be translated.</param> /// <param name="valueFactory">The factory used to instantiate values in the dictionary.</param> void TranslateDictionary<D, T>(ref D dictionary, NodePacketValueFactory<T> valueFactory) where D : IDictionary<string, T>, new() where T : class, INodePacketTranslatable; /// <summary> /// Translates a dictionary of { string, T } for dictionaries with public parameterless constructors. /// </summary> /// <typeparam name="D">The reference type for the dictionary.</typeparam> /// <typeparam name="T">The reference type for values in the dictionary.</typeparam> /// <param name="dictionary">The dictionary to be translated.</param> /// <param name="valueFactory">The factory used to instantiate values in the dictionary.</param> /// <param name="dictionaryCreator">A factory used to create a <see cref="NodePacketDictionaryCreator{D}"/>.</param> void TranslateDictionary<D, T>(ref D dictionary, NodePacketValueFactory<T> valueFactory, NodePacketDictionaryCreator<D> dictionaryCreator) where D : IDictionary<string, T> where T : class, INodePacketTranslatable; /// <summary> /// Translates the boolean that says whether this value is null or not /// </summary> /// <param name="value">The object to test.</param> /// <typeparam name="T">The type of object to test.</typeparam> /// <returns>True if the object should be written, false otherwise.</returns> bool TranslateNullable<T>(T value); } }