/Mono.Cecil/BaseAssemblyResolver.cs
C# | 407 lines | 320 code | 75 blank | 12 comment | 81 complexity | a1f4211bf15d1bab7d181204016f265d MD5 | raw file
1// 2// Author: 3// Jb Evain (jbevain@gmail.com) 4// 5// Copyright (c) 2008 - 2015 Jb Evain 6// Copyright (c) 2008 - 2011 Novell, Inc. 7// 8// Licensed under the MIT/X11 license. 9// 10 11using System; 12using System.Collections.Generic; 13using System.IO; 14using System.Reflection; 15using System.Text; 16 17using Mono.Collections.Generic; 18 19namespace Mono.Cecil { 20 21 public delegate AssemblyDefinition AssemblyResolveEventHandler (object sender, AssemblyNameReference reference); 22 23 public sealed class AssemblyResolveEventArgs : EventArgs { 24 25 readonly AssemblyNameReference reference; 26 27 public AssemblyNameReference AssemblyReference { 28 get { return reference; } 29 } 30 31 public AssemblyResolveEventArgs (AssemblyNameReference reference) 32 { 33 this.reference = reference; 34 } 35 } 36 37#if !NET_CORE 38 [Serializable] 39#endif 40 public sealed class AssemblyResolutionException : FileNotFoundException { 41 42 readonly AssemblyNameReference reference; 43 44 public AssemblyNameReference AssemblyReference { 45 get { return reference; } 46 } 47 48 public AssemblyResolutionException (AssemblyNameReference reference) 49 : this (reference, null) 50 { 51 } 52 53 public AssemblyResolutionException (AssemblyNameReference reference, Exception innerException) 54 : base (string.Format ("Failed to resolve assembly: '{0}'", reference), innerException) 55 { 56 this.reference = reference; 57 } 58 59#if !NET_CORE 60 AssemblyResolutionException ( 61 System.Runtime.Serialization.SerializationInfo info, 62 System.Runtime.Serialization.StreamingContext context) 63 : base (info, context) 64 { 65 } 66#endif 67 } 68 69 public abstract class BaseAssemblyResolver : IAssemblyResolver { 70 71 static readonly bool on_mono = Type.GetType ("Mono.Runtime") != null; 72 73 readonly Collection<string> directories; 74 75#if NET_CORE 76 // Maps file names of available trusted platform assemblies to their full paths. 77 // Internal for testing. 78 internal static readonly Lazy<Dictionary<string, string>> TrustedPlatformAssemblies = new Lazy<Dictionary<string, string>> (CreateTrustedPlatformAssemblyMap); 79#else 80 Collection<string> gac_paths; 81#endif 82 83 public void AddSearchDirectory (string directory) 84 { 85 directories.Add (directory); 86 } 87 88 public void RemoveSearchDirectory (string directory) 89 { 90 directories.Remove (directory); 91 } 92 93 public string [] GetSearchDirectories () 94 { 95 var directories = new string [this.directories.size]; 96 Array.Copy (this.directories.items, directories, directories.Length); 97 return directories; 98 } 99 100 public event AssemblyResolveEventHandler ResolveFailure; 101 102 protected BaseAssemblyResolver () 103 { 104 directories = new Collection<string> (2) { ".", "bin" }; 105 } 106 107 AssemblyDefinition GetAssembly (string file, ReaderParameters parameters) 108 { 109 if (parameters.AssemblyResolver == null) 110 parameters.AssemblyResolver = this; 111 112 return ModuleDefinition.ReadModule (file, parameters).Assembly; 113 } 114 115 public virtual AssemblyDefinition Resolve (AssemblyNameReference name) 116 { 117 return Resolve (name, new ReaderParameters ()); 118 } 119 120 public virtual AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters) 121 { 122 Mixin.CheckName (name); 123 Mixin.CheckParameters (parameters); 124 125 var assembly = SearchDirectory (name, directories, parameters); 126 if (assembly != null) 127 return assembly; 128 129 if (name.IsRetargetable) { 130 // if the reference is retargetable, zero it 131 name = new AssemblyNameReference (name.Name, Mixin.ZeroVersion) { 132 PublicKeyToken = Empty<byte>.Array, 133 }; 134 } 135 136#if NET_CORE 137 assembly = SearchTrustedPlatformAssemblies (name, parameters); 138 if (assembly != null) 139 return assembly; 140#else 141 var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName); 142 var framework_dirs = on_mono 143 ? new [] { framework_dir, Path.Combine (framework_dir, "Facades") } 144 : new [] { framework_dir }; 145 146 if (IsZero (name.Version)) { 147 assembly = SearchDirectory (name, framework_dirs, parameters); 148 if (assembly != null) 149 return assembly; 150 } 151 152 if (name.Name == "mscorlib") { 153 assembly = GetCorlib (name, parameters); 154 if (assembly != null) 155 return assembly; 156 } 157 158 assembly = GetAssemblyInGac (name, parameters); 159 if (assembly != null) 160 return assembly; 161 162 assembly = SearchDirectory (name, framework_dirs, parameters); 163 if (assembly != null) 164 return assembly; 165#endif 166 if (ResolveFailure != null) { 167 assembly = ResolveFailure (this, name); 168 if (assembly != null) 169 return assembly; 170 } 171 172 throw new AssemblyResolutionException (name); 173 } 174 175#if NET_CORE 176 AssemblyDefinition SearchTrustedPlatformAssemblies (AssemblyNameReference name, ReaderParameters parameters) 177 { 178 if (name.IsWindowsRuntime) 179 return null; 180 181 if (TrustedPlatformAssemblies.Value.TryGetValue (name.Name, out string path)) 182 return GetAssembly (path, parameters); 183 184 return null; 185 } 186 187 static Dictionary<string, string> CreateTrustedPlatformAssemblyMap () 188 { 189 var result = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase); 190 191 string paths; 192 193 try { 194 paths = (string) AppDomain.CurrentDomain.GetData ("TRUSTED_PLATFORM_ASSEMBLIES"); 195 } catch { 196 paths = null; 197 } 198 199 if (paths == null) 200 return result; 201 202 foreach (var path in paths.Split (Path.PathSeparator)) 203 if (string.Equals (Path.GetExtension (path), ".dll", StringComparison.OrdinalIgnoreCase)) 204 result [Path.GetFileNameWithoutExtension (path)] = path; 205 206 return result; 207 } 208#endif 209 210 protected virtual AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable<string> directories, ReaderParameters parameters) 211 { 212 var extensions = name.IsWindowsRuntime ? new [] { ".winmd", ".dll" } : new [] { ".exe", ".dll" }; 213 foreach (var directory in directories) { 214 foreach (var extension in extensions) { 215 string file = Path.Combine (directory, name.Name + extension); 216 if (!File.Exists (file)) 217 continue; 218 try { 219 return GetAssembly (file, parameters); 220 } catch (System.BadImageFormatException) { 221 continue; 222 } 223 } 224 } 225 226 return null; 227 } 228 229 static bool IsZero (Version version) 230 { 231 return version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0; 232 } 233 234#if !NET_CORE 235 AssemblyDefinition GetCorlib (AssemblyNameReference reference, ReaderParameters parameters) 236 { 237 var version = reference.Version; 238 var corlib = typeof (object).Assembly.GetName (); 239 if (corlib.Version == version || IsZero (version)) 240 return GetAssembly (typeof (object).Module.FullyQualifiedName, parameters); 241 242 var path = Directory.GetParent ( 243 Directory.GetParent ( 244 typeof (object).Module.FullyQualifiedName).FullName 245 ).FullName; 246 247 if (on_mono) { 248 if (version.Major == 1) 249 path = Path.Combine (path, "1.0"); 250 else if (version.Major == 2) { 251 if (version.MajorRevision == 5) 252 path = Path.Combine (path, "2.1"); 253 else 254 path = Path.Combine (path, "2.0"); 255 } else if (version.Major == 4) 256 path = Path.Combine (path, "4.0"); 257 else 258 throw new NotSupportedException ("Version not supported: " + version); 259 } else { 260 switch (version.Major) { 261 case 1: 262 if (version.MajorRevision == 3300) 263 path = Path.Combine (path, "v1.0.3705"); 264 else 265 path = Path.Combine (path, "v1.1.4322"); 266 break; 267 case 2: 268 path = Path.Combine (path, "v2.0.50727"); 269 break; 270 case 4: 271 path = Path.Combine (path, "v4.0.30319"); 272 break; 273 default: 274 throw new NotSupportedException ("Version not supported: " + version); 275 } 276 } 277 278 var file = Path.Combine (path, "mscorlib.dll"); 279 if (File.Exists (file)) 280 return GetAssembly (file, parameters); 281 282 if (on_mono && Directory.Exists (path + "-api")) { 283 file = Path.Combine (path + "-api", "mscorlib.dll"); 284 if (File.Exists (file)) 285 return GetAssembly (file, parameters); 286 } 287 288 return null; 289 } 290 291 static Collection<string> GetGacPaths () 292 { 293 if (on_mono) 294 return GetDefaultMonoGacPaths (); 295 296 var paths = new Collection<string> (2); 297 var windir = Environment.GetEnvironmentVariable ("WINDIR"); 298 if (windir == null) 299 return paths; 300 301 paths.Add (Path.Combine (windir, "assembly")); 302 paths.Add (Path.Combine (windir, Path.Combine ("Microsoft.NET", "assembly"))); 303 return paths; 304 } 305 306 static Collection<string> GetDefaultMonoGacPaths () 307 { 308 var paths = new Collection<string> (1); 309 var gac = GetCurrentMonoGac (); 310 if (gac != null) 311 paths.Add (gac); 312 313 var gac_paths_env = Environment.GetEnvironmentVariable ("MONO_GAC_PREFIX"); 314 if (string.IsNullOrEmpty (gac_paths_env)) 315 return paths; 316 317 var prefixes = gac_paths_env.Split (Path.PathSeparator); 318 foreach (var prefix in prefixes) { 319 if (string.IsNullOrEmpty (prefix)) 320 continue; 321 322 var gac_path = Path.Combine (Path.Combine (Path.Combine (prefix, "lib"), "mono"), "gac"); 323 if (Directory.Exists (gac_path) && !paths.Contains (gac)) 324 paths.Add (gac_path); 325 } 326 327 return paths; 328 } 329 330 static string GetCurrentMonoGac () 331 { 332 return Path.Combine ( 333 Directory.GetParent ( 334 Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName)).FullName, 335 "gac"); 336 } 337 338 AssemblyDefinition GetAssemblyInGac (AssemblyNameReference reference, ReaderParameters parameters) 339 { 340 if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0) 341 return null; 342 343 if (gac_paths == null) 344 gac_paths = GetGacPaths (); 345 346 if (on_mono) 347 return GetAssemblyInMonoGac (reference, parameters); 348 349 return GetAssemblyInNetGac (reference, parameters); 350 } 351 352 AssemblyDefinition GetAssemblyInMonoGac (AssemblyNameReference reference, ReaderParameters parameters) 353 { 354 for (int i = 0; i < gac_paths.Count; i++) { 355 var gac_path = gac_paths [i]; 356 var file = GetAssemblyFile (reference, string.Empty, gac_path); 357 if (File.Exists (file)) 358 return GetAssembly (file, parameters); 359 } 360 361 return null; 362 } 363 364 AssemblyDefinition GetAssemblyInNetGac (AssemblyNameReference reference, ReaderParameters parameters) 365 { 366 var gacs = new [] { "GAC_MSIL", "GAC_32", "GAC_64", "GAC" }; 367 var prefixes = new [] { string.Empty, "v4.0_" }; 368 369 for (int i = 0; i < gac_paths.Count; i++) { 370 for (int j = 0; j < gacs.Length; j++) { 371 var gac = Path.Combine (gac_paths [i], gacs [j]); 372 var file = GetAssemblyFile (reference, prefixes [i], gac); 373 if (Directory.Exists (gac) && File.Exists (file)) 374 return GetAssembly (file, parameters); 375 } 376 } 377 378 return null; 379 } 380 381 static string GetAssemblyFile (AssemblyNameReference reference, string prefix, string gac) 382 { 383 var gac_folder = new StringBuilder () 384 .Append (prefix) 385 .Append (reference.Version) 386 .Append ("__"); 387 388 for (int i = 0; i < reference.PublicKeyToken.Length; i++) 389 gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2")); 390 391 return Path.Combine ( 392 Path.Combine ( 393 Path.Combine (gac, reference.Name), gac_folder.ToString ()), 394 reference.Name + ".dll"); 395 } 396#endif 397 public void Dispose () 398 { 399 Dispose (true); 400 GC.SuppressFinalize (this); 401 } 402 403 protected virtual void Dispose (bool disposing) 404 { 405 } 406 } 407}