1 // Copyright (c) Microsoft. All rights reserved. 2 // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 //----------------------------------------------------------------------- 4 // </copyright> 5 // <summary>Implementation of Some special translation methods that we 6 // can't put in INodePacketTranslator.</summary> 7 //----------------------------------------------------------------------- 8 9 using System; 10 using System.Collections.Concurrent; 11 using System.Linq; 12 using Microsoft.Build.Collections; 13 using Microsoft.Build.Execution; 14 using Microsoft.Build.Shared; 15 using System.Reflection; 16 using Microsoft.Build.Framework; 17 18 namespace Microsoft.Build.BackEnd 19 { 20 /// <summary> 21 /// This class is responsible for serializing and deserializing anything that is not 22 /// officially supported by INodePacketTranslator, but that we still want to do 23 /// custom translation of. 24 /// </summary> 25 internal static class NodePacketTranslatorExtensions 26 { 27 private static Lazy<ConcurrentDictionary<Type, ConstructorInfo>> parameterlessConstructorCache = new Lazy<ConcurrentDictionary<Type, ConstructorInfo>>(() => new ConcurrentDictionary<Type, ConstructorInfo>()); 28 29 /// <summary> 30 /// Translates a PropertyDictionary of ProjectPropertyInstances. 31 /// </summary> 32 /// <param name="translator">The tranlator doing the translating</param> 33 /// <param name="value">The dictionary to translate.</param> TranslateProjectPropertyInstanceDictionary(this INodePacketTranslator translator, ref PropertyDictionary<ProjectPropertyInstance> value)34 public static void TranslateProjectPropertyInstanceDictionary(this INodePacketTranslator translator, ref PropertyDictionary<ProjectPropertyInstance> value) 35 { 36 if (!translator.TranslateNullable(value)) 37 { 38 return; 39 } 40 41 if (translator.Mode == TranslationDirection.ReadFromStream) 42 { 43 int count = 0; 44 translator.Translate(ref count); 45 46 value = new PropertyDictionary<ProjectPropertyInstance>(count); 47 for (int i = 0; i < count; i++) 48 { 49 ProjectPropertyInstance instance = null; 50 translator.Translate(ref instance, ProjectPropertyInstance.FactoryForDeserialization); 51 value[instance.Name] = instance; 52 } 53 } 54 else // TranslationDirection.WriteToStream 55 { 56 int count = value.Count; 57 translator.Translate(ref count); 58 59 foreach (ProjectPropertyInstance instance in value) 60 { 61 ProjectPropertyInstance instanceForSerialization = instance; 62 translator.Translate(ref instanceForSerialization, ProjectPropertyInstance.FactoryForDeserialization); 63 } 64 } 65 } 66 67 /// <summary> 68 /// Deserialize a type or a subtype by its full name. The type must implement ITranslateable 69 /// </summary> 70 /// <typeparam name="T">Top level type. Serialized types can be of this type, or subtypes</typeparam> 71 /// <returns></returns> FactoryForDeserializingTypeWithName(this INodePacketTranslator translator)72 public static T FactoryForDeserializingTypeWithName<T>(this INodePacketTranslator translator) 73 { 74 string typeName = null; 75 translator.Translate(ref typeName); 76 77 var type = Type.GetType(typeName); 78 ErrorUtilities.VerifyThrowInvalidOperation(type != null, "type cannot be null"); 79 ErrorUtilities.VerifyThrowInvalidOperation( 80 typeof(T).IsAssignableFrom(type), 81 $"{typeName} must be a {typeof(T).FullName}"); 82 ErrorUtilities.VerifyThrowInvalidOperation( 83 typeof(INodePacketTranslatable).IsAssignableFrom(type), 84 $"{typeName} must be a {nameof(INodePacketTranslatable)}"); 85 86 var parameterlessConstructor = parameterlessConstructorCache.Value.GetOrAdd( 87 type, 88 t => 89 { 90 ConstructorInfo constructor = null; 91 #if FEATURE_TYPE_GETCONSTRUCTOR 92 constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null); 93 #else 94 constructor = 95 type 96 .GetTypeInfo() 97 .GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) 98 .FirstOrDefault(c => c.GetParameters().Length == 0); 99 #endif 100 ErrorUtilities.VerifyThrowInvalidOperation( 101 constructor != null, 102 $"{typeName} must have a private parameterless constructor"); 103 return constructor; 104 }); 105 106 var targetInstanceChild = (INodePacketTranslatable) parameterlessConstructor.Invoke(Array.Empty<object>()); 107 108 targetInstanceChild.Translate(translator); 109 110 return (T) targetInstanceChild; 111 } 112 } 113 }