1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections.Generic;
6 using System.ComponentModel.Composition.Primitives;
7 using System.Diagnostics.CodeAnalysis;
8 using System.Globalization;
9 using System.Linq;
10 using System.Reflection;
11 using Microsoft.Internal;
12 using Microsoft.Internal.Collections;
13 
14 namespace System.ComponentModel.Composition.ReflectionModel
15 {
16     internal class ReflectionComposablePart : ComposablePart, ICompositionElement
17     {
18         private readonly ReflectionComposablePartDefinition _definition;
19         private volatile Dictionary<ImportDefinition, object> _importValues = null;
20         private volatile Dictionary<ImportDefinition, ImportingItem> _importsCache = null;
21         private volatile Dictionary<int, ExportingMember> _exportsCache = null;
22         private volatile bool _invokeImportsSatisfied = true;
23         private bool _initialCompositionComplete = false;
24         private volatile object _cachedInstance;
25         private object _lock = new object();
26 
ReflectionComposablePart(ReflectionComposablePartDefinition definition)27         public ReflectionComposablePart(ReflectionComposablePartDefinition definition)
28         {
29             Requires.NotNull(definition, nameof(definition));
30 
31             _definition = definition;
32         }
33 
ReflectionComposablePart(ReflectionComposablePartDefinition definition, object attributedPart)34         public ReflectionComposablePart(ReflectionComposablePartDefinition definition, object attributedPart)
35         {
36             Requires.NotNull(definition, nameof(definition));
37             Requires.NotNull(attributedPart, nameof(attributedPart));
38 
39             _definition = definition;
40 
41             if (attributedPart is ValueType)
42             {
43                 throw new ArgumentException(SR.ArgumentValueType, "attributedPart");
44             }
45             _cachedInstance = attributedPart;
46         }
47 
EnsureRunning()48         protected virtual void EnsureRunning()
49         {
50         }
RequiresRunning()51         protected void RequiresRunning()
52         {
53             EnsureRunning();
54         }
55 
ReleaseInstanceIfNecessary(object instance)56         protected virtual void ReleaseInstanceIfNecessary(object instance)
57         {
58         }
59 
60         private Dictionary<ImportDefinition, object> ImportValues
61         {
62             get
63             {
64                 var value = _importValues;
65                 if(value == null)
66                 {
67                     lock(_lock)
68                     {
69                         value = _importValues;
70                         if(value == null)
71                         {
72                             value = new Dictionary<ImportDefinition, object>();
73                             _importValues = value;
74                         }
75                     }
76                 }
77                 return value;
78             }
79         }
80 
81         private Dictionary<ImportDefinition, ImportingItem> ImportsCache
82         {
83             get
84             {
85                 var value = _importsCache;
86                 if(value == null)
87                 {
88                     lock(_lock)
89                     {
90                         if(value == null)
91                         {
92                             value = new Dictionary<ImportDefinition, ImportingItem>();
93                             _importsCache = value;
94                         }
95                     }
96                 }
97                 return value;
98             }
99         }
100 
101         protected object CachedInstance
102         {
103             get
104             {
105                 lock (_lock)
106                 {
107                     return _cachedInstance;
108                 }
109             }
110         }
111 
112         public ReflectionComposablePartDefinition Definition
113         {
114             get
115             {
116                 RequiresRunning();
117                 return _definition;
118             }
119         }
120 
121         public override IDictionary<string, object> Metadata
122         {
123             get
124             {
125                 RequiresRunning();
126                 return Definition.Metadata;
127             }
128         }
129 
130         public sealed override IEnumerable<ImportDefinition> ImportDefinitions
131         {
132             get
133             {
134                 RequiresRunning();
135                 return Definition.ImportDefinitions;
136             }
137         }
138 
139         public sealed override IEnumerable<ExportDefinition> ExportDefinitions
140         {
141             get
142             {
143                 RequiresRunning();
144                 return Definition.ExportDefinitions;
145             }
146         }
147 
148         string ICompositionElement.DisplayName
149         {
150             get { return GetDisplayName(); }
151         }
152 
153         ICompositionElement ICompositionElement.Origin
154         {
155             get { return Definition; }
156         }
157 
158         // This is the ONLY method which is not executed under the ImportEngine composition lock.
159         // We need to protect all state that is accesses
GetExportedValue(ExportDefinition definition)160         public override object GetExportedValue(ExportDefinition definition)
161         {
162             RequiresRunning();
163             // given the implementation of the ImportEngine, this iwll be called under a lock if the part is still being composed
164             // This is only called outside of the lock when the part is fully composed
165             // based on that we only protect:
166             // _exportsCache - and thus all calls to GetExportingMemberFromDefinition
167             // access to _importValues
168             // access to _initialCompositionComplete
169             // access to _instance
170             Requires.NotNull(definition, nameof(definition));
171 
172             ExportingMember member = null;
173             lock (_lock)
174             {
175                 member = GetExportingMemberFromDefinition(definition);
176                 if (member == null)
177                 {
178                     throw ExceptionBuilder.CreateExportDefinitionNotOnThisComposablePart("definition");
179                 }
180                 EnsureGettable();
181             }
182 
183             return GetExportedValue(member);
184         }
185 
SetImport(ImportDefinition definition, IEnumerable<Export> exports)186         public override void SetImport(ImportDefinition definition, IEnumerable<Export> exports)
187         {
188             RequiresRunning();
189             Requires.NotNull(definition, nameof(definition));
190             Requires.NotNull(exports, nameof(exports));;
191 
192             ImportingItem item = GetImportingItemFromDefinition(definition);
193             if (item == null)
194             {
195                 throw ExceptionBuilder.CreateImportDefinitionNotOnThisComposablePart("definition");
196             }
197 
198             EnsureSettable(definition);
199 
200             // Avoid walking over exports many times
201             Export[] exportsAsArray = exports.AsArray();
202             EnsureCardinality(definition, exportsAsArray);
203 
204             SetImport(item, exportsAsArray);
205         }
206 
Activate()207         public override void Activate()
208         {
209             RequiresRunning();
210 
211             SetNonPrerequisiteImports();
212 
213             // Whenever we are composed/recomposed notify the instance
214             NotifyImportSatisfied();
215             lock (_lock)
216             {
217                 _initialCompositionComplete = true;
218                 _importValues = null;
219                 _importsCache = null;
220             }
221         }
222 
ToString()223         public override string ToString()
224         {
225             return GetDisplayName();
226         }
227 
GetExportedValue(ExportingMember member)228         private object GetExportedValue(ExportingMember member)
229         {
230             object instance = null;
231             if (member.RequiresInstance)
232             {   // Only activate the instance if we actually need to
233 
234                 instance = GetInstanceActivatingIfNeeded();
235             }
236 
237             return member.GetExportedValue(instance, _lock);
238         }
239 
SetImport(ImportingItem item, Export[] exports)240         private void SetImport(ImportingItem item, Export[] exports)
241         {
242             object value = item.CastExportsToImportType(exports);
243 
244             lock (_lock)
245             {
246                 _invokeImportsSatisfied = true;
247                 ImportValues[item.Definition] = value;
248             }
249         }
250 
GetInstanceActivatingIfNeeded()251         private object GetInstanceActivatingIfNeeded()
252         {
253             var cachedInstance = _cachedInstance;
254 
255             if (cachedInstance != null)
256             {
257                 return cachedInstance;
258             }
259             else
260             {
261                 ConstructorInfo constructor = null;
262                 object[] arguments = null;
263                 // determine whether activation is required, and collect necessary information for activation
264                 // we need to do that under a lock
265                 lock (_lock)
266                 {
267                     if (!RequiresActivation())
268                     {
269                         return null;
270                     }
271 
272                     constructor = Definition.GetConstructor();
273                     if (constructor == null)
274                     {
275                         throw new ComposablePartException(
276                             String.Format(CultureInfo.CurrentCulture,
277                                 SR.ReflectionModel_PartConstructorMissing,
278                                 Definition.GetPartType().FullName),
279                             Definition.ToElement());
280                     }
281                     arguments = GetConstructorArguments();
282                 }
283 
284                 // create instance outside of the lock
285                 object createdInstance = CreateInstance(constructor, arguments);
286 
287                 SetPrerequisiteImports();
288 
289                 // set the created instance
290                 if (_cachedInstance == null)
291                 {
292                     lock (_lock)
293                     {
294                         if (_cachedInstance == null)
295                         {
296                             _cachedInstance = createdInstance;
297                             createdInstance = null;
298                         }
299                     }
300                 }
301 
302                 // if the instance has been already set
303                 if (createdInstance == null)
304                 {
305                     ReleaseInstanceIfNecessary(createdInstance);
306                 }
307             }
308 
309             return _cachedInstance;
310         }
311 
GetConstructorArguments()312         private object[] GetConstructorArguments()
313         {
314             ReflectionParameterImportDefinition[] parameterImports = ImportDefinitions.OfType<ReflectionParameterImportDefinition>().ToArray();
315             object[] arguments = new object[parameterImports.Length];
316 
317             UseImportedValues(
318                 parameterImports,
319                 (import, definition, value) =>
320                 {
321                     if (definition.Cardinality == ImportCardinality.ZeroOrMore && !import.ImportType.IsAssignableCollectionType)
322                     {
323                         throw new ComposablePartException(
324                             String.Format(CultureInfo.CurrentCulture,
325                                 SR.ReflectionModel_ImportManyOnParameterCanOnlyBeAssigned,
326                                 Definition.GetPartType().FullName,
327                                 definition.ImportingLazyParameter.Value.Name),
328                             Definition.ToElement());
329                     }
330 
331                     arguments[definition.ImportingLazyParameter.Value.Position] = value;
332                 },
333                 true);
334 
335             return arguments;
336         }
337 
338         // alwayc called under a lock
RequiresActivation()339         private bool RequiresActivation()
340         {
341             // If we have any imports then we need activation
342             // (static imports are not supported)
343             if (ImportDefinitions.Any())
344             {
345                 return true;
346             }
347 
348             // If we have any instance exports, then we also
349             // need activation.
350             return ExportDefinitions.Any(definition =>
351             {
352                 ExportingMember member = GetExportingMemberFromDefinition(definition);
353 
354                 return member.RequiresInstance;
355             });
356         }
357 
358         // this is called under a lock
EnsureGettable()359         private void EnsureGettable()
360         {
361             // If we're already composed then we know that
362             // all pre-req imports have been satisfied
363             if (_initialCompositionComplete)
364             {
365                 return;
366             }
367 
368             // Make sure all pre-req imports have been set
369             foreach (ImportDefinition definition in ImportDefinitions.Where(definition => definition.IsPrerequisite))
370             {
371                 if (_importValues == null || !ImportValues.ContainsKey(definition))
372                 {
373                     throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
374                                                             SR.InvalidOperation_GetExportedValueBeforePrereqImportSet,
375                                                             definition.ToElement().DisplayName));
376                 }
377             }
378         }
379 
EnsureSettable(ImportDefinition definition)380         private void EnsureSettable(ImportDefinition definition)
381         {
382             lock (_lock)
383             {
384                 if (_initialCompositionComplete && !definition.IsRecomposable)
385                 {
386                     throw new InvalidOperationException(SR.InvalidOperation_DefinitionCannotBeRecomposed);
387                 }
388             }
389         }
390 
EnsureCardinality(ImportDefinition definition, Export[] exports)391         private static void EnsureCardinality(ImportDefinition definition, Export[] exports)
392         {
393             Requires.NullOrNotNullElements(exports, "exports");
394 
395             ExportCardinalityCheckResult result = ExportServices.CheckCardinality(definition, exports);
396 
397             switch (result)
398             {
399                 case ExportCardinalityCheckResult.NoExports:
400                     throw new ArgumentException(SR.Argument_ExportsEmpty, "exports");
401 
402                 case ExportCardinalityCheckResult.TooManyExports:
403                     throw new ArgumentException(SR.Argument_ExportsTooMany, "exports");
404 
405                 default:
406                     Assumes.IsTrue(result == ExportCardinalityCheckResult.Match);
407                     break;
408             }
409         }
410 
CreateInstance(ConstructorInfo constructor, object[] arguments)411         private object CreateInstance(ConstructorInfo constructor, object[] arguments)
412         {
413             Exception exception = null;
414             object instance = null;
415 
416             try
417             {
418                 instance = constructor.SafeInvoke(arguments);
419             }
420             catch (TypeInitializationException ex)
421             {
422                 exception = ex;
423             }
424             catch (TargetInvocationException ex)
425             {
426                 exception = ex.InnerException;
427             }
428 
429             if (exception != null)
430             {
431                 throw new ComposablePartException(
432                     String.Format(CultureInfo.CurrentCulture,
433                         SR.ReflectionModel_PartConstructorThrewException,
434                         Definition.GetPartType().FullName),
435                     Definition.ToElement(),
436                     exception);
437             }
438 
439             return instance;
440         }
441 
SetNonPrerequisiteImports()442         private void SetNonPrerequisiteImports()
443         {
444             IEnumerable<ImportDefinition> members = ImportDefinitions.Where(import => !import.IsPrerequisite);
445 
446             // NOTE: Dev10 484204 The validation is turned off for post imports because of it broke declarative composition
447             UseImportedValues(members, SetExportedValueForImport, false);
448         }
449 
SetPrerequisiteImports()450         private void SetPrerequisiteImports()
451         {
452             IEnumerable<ImportDefinition> members = ImportDefinitions.Where(import => import.IsPrerequisite);
453 
454             // NOTE: Dev10 484204 The validation is turned off for post imports because of it broke declarative composition
455             UseImportedValues(members, SetExportedValueForImport, false);
456         }
457 
SetExportedValueForImport(ImportingItem import, ImportDefinition definition, object value)458         private void SetExportedValueForImport(ImportingItem import, ImportDefinition definition, object value)
459         {
460             ImportingMember importMember = (ImportingMember)import;
461 
462             object instance = GetInstanceActivatingIfNeeded();
463 
464             importMember.SetExportedValue(instance, value);
465         }
466 
467         private void UseImportedValues<TImportDefinition>(IEnumerable<TImportDefinition> definitions, Action<ImportingItem, TImportDefinition, object> useImportValue, bool errorIfMissing)
468             where TImportDefinition : ImportDefinition
469         {
470             var result = CompositionResult.SucceededResult;
471 
472             foreach (var definition in definitions)
473             {
474                 ImportingItem import = GetImportingItemFromDefinition(definition);
475 
476                 object value;
477                 if (!TryGetImportValue(definition, out value))
478                 {
479                     if (!errorIfMissing)
480                     {
481                         continue;
482                     }
483 
484                     if (definition.Cardinality == ImportCardinality.ExactlyOne)
485                     {
486                         var error = CompositionError.Create(
487                             CompositionErrorId.ImportNotSetOnPart,
488                             SR.ImportNotSetOnPart,
489                             Definition.GetPartType().FullName,
490                             definition.ToString());
491                         result = result.MergeError(error);
492                         continue;
493                     }
494                     else
495                     {
496                         value = import.CastExportsToImportType(new Export[0]);
497                     }
498                 }
499 
500                 useImportValue(import, definition, value);
501             }
502 
503             result.ThrowOnErrors();
504         }
505 
TryGetImportValue(ImportDefinition definition, out object value)506         private bool TryGetImportValue(ImportDefinition definition, out object value)
507         {
508             lock (_lock)
509             {
510                 if (_importValues != null && ImportValues.TryGetValue(definition, out value))
511                 {
512                     ImportValues.Remove(definition);
513                     return true;
514                 }
515             }
516 
517             value = null;
518             return false;
519         }
520 
521         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
NotifyImportSatisfied()522         private void NotifyImportSatisfied()
523         {
524             if (_invokeImportsSatisfied)
525             {
526                 IPartImportsSatisfiedNotification notify = GetInstanceActivatingIfNeeded() as IPartImportsSatisfiedNotification;
527 
528                 lock (_lock)
529                 {
530                     if (!_invokeImportsSatisfied)
531                     {
532                         //Already notified on another thread
533                         return;
534                     }
535                     _invokeImportsSatisfied = false;
536                 }
537 
538                 if (notify != null)
539                 {
540                     try
541                     {
542                         notify.OnImportsSatisfied();
543                     }
544                     catch (Exception exception)
545                     {
546                         throw new ComposablePartException(
547                             String.Format(CultureInfo.CurrentCulture,
548                                 SR.ReflectionModel_PartOnImportsSatisfiedThrewException,
549                                 Definition.GetPartType().FullName),
550                             Definition.ToElement(),
551                             exception);
552                     }
553                 }
554             }
555         }
556 
557         // this is always called under a lock
GetExportingMemberFromDefinition(ExportDefinition definition)558         private ExportingMember GetExportingMemberFromDefinition(ExportDefinition definition)
559         {
560             ExportingMember result;
561             ReflectionMemberExportDefinition reflectionExport = definition as ReflectionMemberExportDefinition;
562             if (reflectionExport == null)
563             {
564                 return null;
565             }
566 
567             int exportIndex = reflectionExport.GetIndex();
568             if(_exportsCache == null)
569             {
570                 _exportsCache = new Dictionary<int, ExportingMember>();
571             }
572             if (!_exportsCache.TryGetValue(exportIndex, out result))
573             {
574                 result = GetExportingMember(definition);
575                 if (result != null)
576                 {
577                     _exportsCache[exportIndex] = result;
578                 }
579             }
580 
581             return result;
582         }
583 
GetImportingItemFromDefinition(ImportDefinition definition)584         private ImportingItem GetImportingItemFromDefinition(ImportDefinition definition)
585         {
586             ImportingItem result;
587             if (!ImportsCache.TryGetValue(definition, out result))
588             {
589                 result = GetImportingItem(definition);
590                 if (result != null)
591                 {
592                     ImportsCache[definition] = result;
593                 }
594             }
595 
596             return result;
597         }
598 
GetImportingItem(ImportDefinition definition)599         private static ImportingItem GetImportingItem(ImportDefinition definition)
600         {
601             ReflectionImportDefinition reflectionDefinition = definition as ReflectionImportDefinition;
602             if (reflectionDefinition != null)
603             {
604                 return reflectionDefinition.ToImportingItem();
605             }
606             // Don't recognize it
607             return null;
608         }
609 
GetExportingMember(ExportDefinition definition)610         private static ExportingMember GetExportingMember(ExportDefinition definition)
611         {
612             ReflectionMemberExportDefinition exportDefinition = definition as ReflectionMemberExportDefinition;
613             if (exportDefinition != null)
614             {
615                 return exportDefinition.ToExportingMember();
616             }
617 
618             // Don't recognize it
619             return null;
620         }
621 
GetDisplayName()622         private string GetDisplayName()
623         {
624             return _definition.GetPartType().GetDisplayName();
625         }
626     }
627 }
628