1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 namespace IceInternal
6 {
7     using System;
8     using System.Runtime.InteropServices;
9     using System.Collections;
10     using System.Collections.Generic;
11     using System.Reflection;
12 
13     public sealed class AssemblyUtil
14     {
15 #if NETSTANDARD2_0
16         public static readonly bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
17         public static readonly bool isMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
18         public static readonly bool isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
19         public static readonly bool isMono = RuntimeInformation.FrameworkDescription.Contains("Mono");
20 #else
21         public static readonly bool isWindows = true;
22         public static readonly bool isMacOS = false;
23         public static readonly bool isLinux = false;
24         public static readonly bool isMono = false;
25 #endif
findType(Instance instance, string csharpId)26         public static Type findType(Instance instance, string csharpId)
27         {
28             lock(_mutex)
29             {
30                 Type t;
31                 if (_typeTable.TryGetValue(csharpId, out t))
32                 {
33                     return t;
34                 }
35 
36                 loadAssemblies(); // Lazy initialization
37                 foreach (Assembly a in _loadedAssemblies.Values)
38                 {
39                     if((t = a.GetType(csharpId)) != null)
40                     {
41                         _typeTable[csharpId] = t;
42                         return t;
43                     }
44                 }
45             }
46             return null;
47         }
48 
findTypesWithPrefix(string prefix)49         public static Type[] findTypesWithPrefix(string prefix)
50         {
51             LinkedList<Type> l = new LinkedList<Type>();
52 
53             lock(_mutex)
54             {
55                 loadAssemblies(); // Lazy initialization
56                 foreach(Assembly a in _loadedAssemblies.Values)
57                 {
58                     try
59                     {
60                         Type[] types = a.GetTypes();
61                         foreach(Type t in types)
62                         {
63                             if(t.AssemblyQualifiedName.IndexOf(prefix, StringComparison.Ordinal) == 0)
64                             {
65                                 l.AddLast(t);
66                             }
67                         }
68                     }
69                     catch(ReflectionTypeLoadException)
70                     {
71                         // Failed to load types from the assembly, ignore and continue
72                     }
73                 }
74             }
75 
76             Type[] result = new Type[l.Count];
77             if(l.Count > 0)
78             {
79                 l.CopyTo(result, 0);
80             }
81             return result;
82         }
83 
createInstance(Type t)84         public static object createInstance(Type t)
85         {
86             try
87             {
88                 return Activator.CreateInstance(t);
89             }
90             catch(MemberAccessException)
91             {
92                 return null;
93             }
94         }
95 
preloadAssemblies()96         public static void preloadAssemblies()
97         {
98             lock(_mutex)
99             {
100                 loadAssemblies(); // Lazy initialization
101             }
102         }
103 
104         //
105         // Make sure that all assemblies that are referenced by this process
106         // are actually loaded. This is necessary so we can use reflection
107         // on any type in any assembly because the type we are after will
108         // most likely not be in the current assembly and, worse, may be
109         // in an assembly that has not been loaded yet. (Type.GetType()
110         // is no good because it looks only in the calling object's assembly
111         // and mscorlib.dll.)
112         //
loadAssemblies()113         private static void loadAssemblies()
114         {
115             Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
116             List<Assembly> newAssemblies = null;
117             foreach(Assembly a in assemblies)
118             {
119                 if(!_loadedAssemblies.Contains(a.FullName))
120                 {
121                     if(newAssemblies == null)
122                     {
123                         newAssemblies = new List<Assembly>();
124                     }
125                     newAssemblies.Add(a);
126                     _loadedAssemblies[a.FullName] = a;
127                 }
128             }
129             if(newAssemblies != null)
130             {
131                 foreach(Assembly a in newAssemblies)
132                 {
133                     loadReferencedAssemblies(a);
134                 }
135             }
136         }
137 
loadReferencedAssemblies(Assembly a)138         private static void loadReferencedAssemblies(Assembly a)
139         {
140             try
141             {
142                 AssemblyName[] names = a.GetReferencedAssemblies();
143                 foreach(AssemblyName name in names)
144                 {
145                     if(!_loadedAssemblies.ContainsKey(name.FullName))
146                     {
147                         try
148                         {
149                             Assembly ra = Assembly.Load(name);
150                             //
151                             // The value of name.FullName may not match that of ra.FullName, so
152                             // we record the assembly using both keys.
153                             //
154                             _loadedAssemblies[name.FullName] = ra;
155                             _loadedAssemblies[ra.FullName] = ra;
156                             loadReferencedAssemblies(ra);
157                         }
158                         catch(Exception)
159                         {
160                             // Ignore assemblies that cannot be loaded.
161                         }
162                     }
163                 }
164             }
165             catch(PlatformNotSupportedException)
166             {
167                 // Some platforms like UWP do not support using GetReferencedAssemblies
168             }
169         }
170 
171         private static Hashtable _loadedAssemblies = new Hashtable(); // <string, Assembly> pairs.
172         private static Dictionary<string, Type> _typeTable = new Dictionary<string, Type>(); // <type name, Type> pairs.
173         private static object _mutex = new object();
174     }
175 }
176