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 }