88using Microsoft . Build . Framework ;
99using Microsoft . Build . Shared ;
1010
11- #nullable disable
12-
1311namespace Microsoft . Build . Execution
1412{
1513 /// <summary>
@@ -19,30 +17,50 @@ internal sealed class TaskFactoryWrapper
1917 {
2018 #region Data
2119
20+ private struct PropertyData
21+ {
22+ /// <summary>
23+ /// Cache of names of required properties on this type
24+ /// </summary>
25+ public readonly IReadOnlyDictionary < string , string > NamesOfPropertiesWithRequiredAttribute ;
26+
27+ /// <summary>
28+ /// Cache of names of output properties on this type
29+ /// </summary>
30+ public readonly IReadOnlyDictionary < string , string > NamesOfPropertiesWithOutputAttribute ;
31+
32+ /// <summary>
33+ /// Cache of names of properties on this type whose names are ambiguous
34+ /// </summary>
35+ public readonly IReadOnlyDictionary < string , string > NamesOfPropertiesWithAmbiguousMatches ;
36+
37+ /// <summary>
38+ /// Cache of PropertyInfos for this type
39+ /// </summary>
40+ public readonly IReadOnlyDictionary < string , TaskPropertyInfo > PropertyInfoCache ;
41+
42+ public PropertyData (
43+ IReadOnlyDictionary < string , string > namesOfPropertiesWithRequiredAttribute ,
44+ IReadOnlyDictionary < string , string > namesOfPropertiesWithOutputAttribute ,
45+ IReadOnlyDictionary < string , string > namesOfPropertiesWithAmbiguousMatches ,
46+ IReadOnlyDictionary < string , TaskPropertyInfo > propertyInfoCache )
47+ {
48+ NamesOfPropertiesWithRequiredAttribute = namesOfPropertiesWithRequiredAttribute ;
49+ NamesOfPropertiesWithOutputAttribute = namesOfPropertiesWithOutputAttribute ;
50+ NamesOfPropertiesWithAmbiguousMatches = namesOfPropertiesWithAmbiguousMatches ;
51+ PropertyInfoCache = propertyInfoCache ;
52+ }
53+ }
54+
2255 /// <summary>
2356 /// Factory which is wrapped by the wrapper
2457 /// </summary>
2558 private ITaskFactory _taskFactory ;
2659
2760 /// <summary>
28- /// Cache of names of required properties on this type
29- /// </summary>
30- private IDictionary < string , string > _namesOfPropertiesWithRequiredAttribute ;
31-
32- /// <summary>
33- /// Cache of names of output properties on this type
34- /// </summary>
35- private IDictionary < string , string > _namesOfPropertiesWithOutputAttribute ;
36-
37- /// <summary>
38- /// Cache of names of properties on this type whose names are ambiguous
39- /// </summary>
40- private IDictionary < string , string > _namesOfPropertiesWithAmbiguousMatches ;
41-
42- /// <summary>
43- /// Cache of PropertyInfos for this type
61+ /// Wrapper of lazy initializable property data.
4462 /// </summary>
45- private IDictionary < string , TaskPropertyInfo > _propertyInfoCache ;
63+ private Lazy < PropertyData > _propertyData ;
4664
4765 /// <summary>
4866 /// The name of the task this factory can create.
@@ -70,6 +88,7 @@ internal TaskFactoryWrapper(ITaskFactory taskFactory, LoadedType taskFactoryLoad
7088 _taskName = taskName ;
7189 TaskFactoryLoadedType = taskFactoryLoadInfo ;
7290 _factoryIdentityParameters = factoryIdentityParameters ;
91+ _propertyData = new Lazy < PropertyData > ( PopulatePropertyInfo ) ;
7392 }
7493
7594 #endregion
@@ -101,13 +120,11 @@ public ITaskFactory TaskFactory
101120 /// Caches the result - since it can't change during the build.
102121 /// </summary>
103122 /// <returns></returns>
104- public IDictionary < string , string > GetNamesOfPropertiesWithRequiredAttribute
123+ public IReadOnlyDictionary < string , string > GetNamesOfPropertiesWithRequiredAttribute
105124 {
106125 get
107126 {
108- PopulatePropertyInfoCacheIfNecessary ( ) ;
109-
110- return _namesOfPropertiesWithRequiredAttribute ;
127+ return _propertyData . Value . NamesOfPropertiesWithRequiredAttribute ;
111128 }
112129 }
113130
@@ -116,13 +133,11 @@ public IDictionary<string, string> GetNamesOfPropertiesWithRequiredAttribute
116133 /// Caches the result - since it can't change during the build.
117134 /// </summary>
118135 /// <returns></returns>
119- public IDictionary < string , string > GetNamesOfPropertiesWithOutputAttribute
136+ public IReadOnlyDictionary < string , string > GetNamesOfPropertiesWithOutputAttribute
120137 {
121138 get
122139 {
123- PopulatePropertyInfoCacheIfNecessary ( ) ;
124-
125- return _namesOfPropertiesWithOutputAttribute ;
140+ return _propertyData . Value . NamesOfPropertiesWithOutputAttribute ;
126141 }
127142 }
128143
@@ -158,18 +173,16 @@ public IDictionary<string, string> FactoryIdentityParameters
158173 /// </summary>
159174 /// <param name="propertyName">property name</param>
160175 /// <returns>PropertyInfo</returns>
161- public TaskPropertyInfo GetProperty ( string propertyName )
176+ public TaskPropertyInfo ? GetProperty ( string propertyName )
162177 {
163- PopulatePropertyInfoCacheIfNecessary ( ) ;
164-
165- TaskPropertyInfo propertyInfo ;
166- if ( ! _propertyInfoCache . TryGetValue ( propertyName , out propertyInfo ) )
178+ TaskPropertyInfo ? propertyInfo ;
179+ if ( ! _propertyData . Value . PropertyInfoCache . TryGetValue ( propertyName , out propertyInfo ) )
167180 {
168181 return null ;
169182 }
170183 else
171184 {
172- if ( _namesOfPropertiesWithAmbiguousMatches . ContainsKey ( propertyName ) )
185+ if ( _propertyData . Value . NamesOfPropertiesWithAmbiguousMatches . ContainsKey ( propertyName ) )
173186 {
174187 // See comment in PopulatePropertyInfoCache
175188 throw new AmbiguousMatchException ( ) ;
@@ -187,37 +200,37 @@ internal void SetPropertyValue(ITask task, TaskPropertyInfo property, object val
187200 ErrorUtilities . VerifyThrowArgumentNull ( task , nameof ( task ) ) ;
188201 ErrorUtilities . VerifyThrowArgumentNull ( property , nameof ( property ) ) ;
189202
190- IGeneratedTask generatedTask = task as IGeneratedTask ;
203+ IGeneratedTask ? generatedTask = task as IGeneratedTask ;
191204 if ( generatedTask != null )
192205 {
193206 generatedTask . SetPropertyValue ( property , value ) ;
194207 }
195208 else
196209 {
197210 ReflectableTaskPropertyInfo propertyInfo = ( ReflectableTaskPropertyInfo ) property ;
198- propertyInfo . Reflection . SetValue ( task , value , null ) ;
211+ propertyInfo . Reflection ? . SetValue ( task , value , null ) ;
199212 }
200213 }
201214
202215 /// <summary>
203216 /// Gets the value of a given property on the given task.
204217 /// </summary>
205- internal object GetPropertyValue ( ITask task , TaskPropertyInfo property )
218+ internal object ? GetPropertyValue ( ITask task , TaskPropertyInfo property )
206219 {
207220 ErrorUtilities . VerifyThrowArgumentNull ( task , nameof ( task ) ) ;
208221 ErrorUtilities . VerifyThrowArgumentNull ( property , nameof ( property ) ) ;
209222
210- IGeneratedTask generatedTask = task as IGeneratedTask ;
223+ IGeneratedTask ? generatedTask = task as IGeneratedTask ;
211224 if ( generatedTask != null )
212225 {
213226 return generatedTask . GetPropertyValue ( property ) ;
214227 }
215228 else
216229 {
217- ReflectableTaskPropertyInfo propertyInfo = property as ReflectableTaskPropertyInfo ;
230+ ReflectableTaskPropertyInfo ? propertyInfo = property as ReflectableTaskPropertyInfo ;
218231 if ( propertyInfo != null )
219232 {
220- return propertyInfo . Reflection . GetValue ( task , null ) ;
233+ return propertyInfo . Reflection ? . GetValue ( task , null ) ;
221234 }
222235 else
223236 {
@@ -242,77 +255,79 @@ internal bool IsCreatableByFactory(string taskName)
242255 /// <summary>
243256 /// Populate the cache of PropertyInfos for this type
244257 /// </summary>
245- private void PopulatePropertyInfoCacheIfNecessary ( )
258+ private PropertyData PopulatePropertyInfo ( )
246259 {
247- if ( _propertyInfoCache == null )
260+ Dictionary < string , TaskPropertyInfo > ? propertyInfoCache = null ;
261+ Dictionary < string , string > ? namesOfPropertiesWithRequiredAttribute = null ;
262+ Dictionary < string , string > ? namesOfPropertiesWithOutputAttribute = null ;
263+ Dictionary < string , string > ? namesOfPropertiesWithAmbiguousMatches = null ;
264+
265+ bool taskTypeImplementsIGeneratedTask = typeof ( IGeneratedTask ) . IsAssignableFrom ( _taskFactory . TaskType ) ;
266+ TaskPropertyInfo [ ] propertyInfos = _taskFactory . GetTaskParameters ( ) ;
267+
268+ for ( int i = 0 ; i < propertyInfos . Length ; i ++ )
248269 {
249- bool taskTypeImplementsIGeneratedTask = typeof ( IGeneratedTask ) . IsAssignableFrom ( _taskFactory . TaskType ) ;
250- TaskPropertyInfo [ ] propertyInfos = _taskFactory . GetTaskParameters ( ) ;
270+ // If the task implements IGeneratedTask, we must use the TaskPropertyInfo the factory gives us.
271+ // Otherwise, we never have to hand the TaskPropertyInfo back to the task or factory, so we replace
272+ // theirs with one of our own that will allow us to cache reflection data per-property.
273+ TaskPropertyInfo propertyInfo = propertyInfos [ i ] ;
274+ if ( ! taskTypeImplementsIGeneratedTask )
275+ {
276+ propertyInfo = new ReflectableTaskPropertyInfo ( propertyInfo , _taskFactory . TaskType ) ;
277+ }
251278
252- for ( int i = 0 ; i < propertyInfos . Length ; i ++ )
279+ try
253280 {
254- // If the task implements IGeneratedTask, we must use the TaskPropertyInfo the factory gives us.
255- // Otherwise, we never have to hand the TaskPropertyInfo back to the task or factory, so we replace
256- // theirs with one of our own that will allow us to cache reflection data per-property.
257- TaskPropertyInfo propertyInfo = propertyInfos [ i ] ;
258- if ( ! taskTypeImplementsIGeneratedTask )
281+ if ( propertyInfoCache == null )
259282 {
260- propertyInfo = new ReflectableTaskPropertyInfo ( propertyInfo , _taskFactory . TaskType ) ;
283+ propertyInfoCache = new Dictionary < string , TaskPropertyInfo > ( StringComparer . OrdinalIgnoreCase ) ;
261284 }
262285
263- try
264- {
265- if ( _propertyInfoCache == null )
266- {
267- _propertyInfoCache = new Dictionary < string , TaskPropertyInfo > ( StringComparer . OrdinalIgnoreCase ) ;
268- }
269-
270- _propertyInfoCache . Add ( propertyInfo . Name , propertyInfo ) ;
271- }
272- catch ( ArgumentException )
286+ propertyInfoCache . Add ( propertyInfo . Name , propertyInfo ) ;
287+ }
288+ catch ( ArgumentException )
289+ {
290+ // We have encountered a duplicate entry in our hashtable; if we had used BindingFlags.IgnoreCase this
291+ // would have produced an AmbiguousMatchException. In the old code, before this cache existed,
292+ // that wouldn't have been thrown unless and until the project actually tried to set this ambiguous parameter.
293+ // So rather than fail here, we store a list of ambiguous names and throw later, when one of them
294+ // is requested.
295+ if ( namesOfPropertiesWithAmbiguousMatches == null )
273296 {
274- // We have encountered a duplicate entry in our hashtable; if we had used BindingFlags.IgnoreCase this
275- // would have produced an AmbiguousMatchException. In the old code, before this cache existed,
276- // that wouldn't have been thrown unless and until the project actually tried to set this ambiguous parameter.
277- // So rather than fail here, we store a list of ambiguous names and throw later, when one of them
278- // is requested.
279- if ( _namesOfPropertiesWithAmbiguousMatches == null )
280- {
281- _namesOfPropertiesWithAmbiguousMatches = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
282- }
283-
284- _namesOfPropertiesWithAmbiguousMatches [ propertyInfo . Name ] = String . Empty ;
297+ namesOfPropertiesWithAmbiguousMatches = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
285298 }
286299
287- if ( propertyInfos [ i ] . Required )
288- {
289- if ( _namesOfPropertiesWithRequiredAttribute == null )
290- {
291- _namesOfPropertiesWithRequiredAttribute = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
292- }
293-
294- // we have a require attribute defined, keep a record of that
295- _namesOfPropertiesWithRequiredAttribute [ propertyInfo . Name ] = String . Empty ;
296- }
300+ namesOfPropertiesWithAmbiguousMatches [ propertyInfo . Name ] = String . Empty ;
301+ }
297302
298- if ( propertyInfos [ i ] . Output )
303+ if ( propertyInfos [ i ] . Required )
304+ {
305+ if ( namesOfPropertiesWithRequiredAttribute == null )
299306 {
300- if ( _namesOfPropertiesWithOutputAttribute == null )
301- {
302- _namesOfPropertiesWithOutputAttribute = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
303- }
304-
305- // we have a output attribute defined, keep a record of that
306- _namesOfPropertiesWithOutputAttribute [ propertyInfo . Name ] = String . Empty ;
307+ namesOfPropertiesWithRequiredAttribute = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
307308 }
309+
310+ // we have a require attribute defined, keep a record of that
311+ namesOfPropertiesWithRequiredAttribute [ propertyInfo . Name ] = String . Empty ;
308312 }
309313
310- _propertyInfoCache ??= ReadOnlyEmptyDictionary < string , TaskPropertyInfo > . Instance ;
314+ if ( propertyInfos [ i ] . Output )
315+ {
316+ if ( namesOfPropertiesWithOutputAttribute == null )
317+ {
318+ namesOfPropertiesWithOutputAttribute = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
319+ }
311320
312- _namesOfPropertiesWithRequiredAttribute ??= ReadOnlyEmptyDictionary < string , string > . Instance ;
313- _namesOfPropertiesWithOutputAttribute ??= ReadOnlyEmptyDictionary < string , string > . Instance ;
314- _namesOfPropertiesWithAmbiguousMatches ??= ReadOnlyEmptyDictionary < string , string > . Instance ;
321+ // we have a output attribute defined, keep a record of that
322+ namesOfPropertiesWithOutputAttribute [ propertyInfo . Name ] = String . Empty ;
323+ }
315324 }
325+
326+ return new PropertyData (
327+ ( IReadOnlyDictionary < string , string > ? ) namesOfPropertiesWithRequiredAttribute ?? ReadOnlyEmptyDictionary < string , string > . Instance ,
328+ ( IReadOnlyDictionary < string , string > ? ) namesOfPropertiesWithOutputAttribute ?? ReadOnlyEmptyDictionary < string , string > . Instance ,
329+ ( IReadOnlyDictionary < string , string > ? ) namesOfPropertiesWithAmbiguousMatches ?? ReadOnlyEmptyDictionary < string , string > . Instance ,
330+ ( IReadOnlyDictionary < string , TaskPropertyInfo > ? ) propertyInfoCache ?? ReadOnlyEmptyDictionary < string , TaskPropertyInfo > . Instance ) ;
316331 }
317332 #endregion
318333 }
0 commit comments