PageRenderTime 42ms CodeModel.GetById 19ms app.highlight 18ms RepoModel.GetById 2ms app.codeStats 0ms

/Mono.Cecil/BaseAssemblyResolver.cs

http://github.com/jbevain/cecil
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}