update Cecil
[mono.git] / mcs / class / Mono.Cecil / Mono.Cecil / BaseAssemblyResolver.cs
index 7a4ac1b5c328315452282e0e589947e277e6e523..10ab2c34a868cecfb0569888cf9a8d8e9645e71d 100644 (file)
@@ -4,7 +4,7 @@
 // Author:
 //   Jb Evain (jbevain@gmail.com)
 //
-// (C) 2005 Jb Evain
+// Copyright (c) 2008 - 2011 Jb Evain
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+using Mono.Collections.Generic;
+
 namespace Mono.Cecil {
 
-       using System;
-       using System.Collections;
-       using System.IO;
-       using SR = System.Reflection;
-       using System.Text;
+       public delegate AssemblyDefinition AssemblyResolveEventHandler (object sender, AssemblyNameReference reference);
+
+       public sealed class AssemblyResolveEventArgs : EventArgs {
+
+               readonly AssemblyNameReference reference;
+
+               public AssemblyNameReference AssemblyReference {
+                       get { return reference; }
+               }
+
+               public AssemblyResolveEventArgs (AssemblyNameReference reference)
+               {
+                       this.reference = reference;
+               }
+       }
+
+#if !SILVERLIGHT && !CF
+       [Serializable]
+#endif
+       public class AssemblyResolutionException : FileNotFoundException {
+
+               readonly AssemblyNameReference reference;
+
+               public AssemblyNameReference AssemblyReference {
+                       get { return reference; }
+               }
+
+               public AssemblyResolutionException (AssemblyNameReference reference)
+                       : base (string.Format ("Failed to resolve assembly: '{0}'", reference))
+               {
+                       this.reference = reference;
+               }
+
+#if !SILVERLIGHT && !CF
+               protected AssemblyResolutionException (
+                       System.Runtime.Serialization.SerializationInfo info,
+                       System.Runtime.Serialization.StreamingContext context)
+                       : base (info, context)
+               {
+               }
+#endif
+       }
 
        public abstract class BaseAssemblyResolver : IAssemblyResolver {
 
-               ArrayList m_directories;
+               static readonly bool on_mono = Type.GetType ("Mono.Runtime") != null;
+
+               readonly Collection<string> directories;
+
+#if !SILVERLIGHT && !CF
+               Collection<string> gac_paths;
+#endif
 
                public void AddSearchDirectory (string directory)
                {
-                       m_directories.Add (directory);
+                       directories.Add (directory);
                }
 
                public void RemoveSearchDirectory (string directory)
                {
-                       m_directories.Remove (directory);
+                       directories.Remove (directory);
                }
 
                public string [] GetSearchDirectories ()
                {
-                       return (string []) m_directories.ToArray (typeof (string));
+                       var directories = new string [this.directories.size];
+                       Array.Copy (this.directories.items, directories, directories.Length);
+                       return directories;
                }
 
                public virtual AssemblyDefinition Resolve (string fullName)
                {
-                       return Resolve (AssemblyNameReference.Parse (fullName));
+                       return Resolve (fullName, new ReaderParameters ());
+               }
+
+               public virtual AssemblyDefinition Resolve (string fullName, ReaderParameters parameters)
+               {
+                       if (fullName == null)
+                               throw new ArgumentNullException ("fullName");
+
+                       return Resolve (AssemblyNameReference.Parse (fullName), parameters);
+               }
+
+               public event AssemblyResolveEventHandler ResolveFailure;
+
+               protected BaseAssemblyResolver ()
+               {
+                       directories = new Collection<string> (2) { ".", "bin" };
                }
 
-               public BaseAssemblyResolver ()
+               AssemblyDefinition GetAssembly (string file, ReaderParameters parameters)
                {
-                       m_directories = new ArrayList ();
-                       m_directories.Add (".");
-                       m_directories.Add ("bin");
+                       if (parameters.AssemblyResolver == null)
+                               parameters.AssemblyResolver = this;
+
+                       return ModuleDefinition.ReadModule (file, parameters).Assembly;
                }
 
                public virtual AssemblyDefinition Resolve (AssemblyNameReference name)
                {
-                       AssemblyDefinition assembly;
-                       string frameworkdir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName);
+                       return Resolve (name, new ReaderParameters ());
+               }
+
+               public virtual AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
+               {
+                       if (name == null)
+                               throw new ArgumentNullException ("name");
+                       if (parameters == null)
+                               parameters = new ReaderParameters ();
 
-                       assembly = SearchDirectory (name, m_directories);
+                       var assembly = SearchDirectory (name, directories, parameters);
                        if (assembly != null)
                                return assembly;
 
+#if !SILVERLIGHT && !CF
+                       var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName);
+
                        if (IsZero (name.Version)) {
-                               assembly = SearchDirectory (name, new string [] {frameworkdir});
+                               assembly = SearchDirectory (name, new [] { framework_dir }, parameters);
                                if (assembly != null)
                                        return assembly;
                        }
 
-#if !CF_1_0 && !CF_2_0
                        if (name.Name == "mscorlib") {
-                               assembly = GetCorlib (name);
+                               assembly = GetCorlib (name, parameters);
                                if (assembly != null)
                                        return assembly;
                        }
 
-                       assembly = GetAssemblyInGac (name);
+                       assembly = GetAssemblyInGac (name, parameters);
                        if (assembly != null)
                                return assembly;
-#endif
 
-                       assembly = SearchDirectory (name, new string [] {frameworkdir});
+                       assembly = SearchDirectory (name, new [] { framework_dir }, parameters);
                        if (assembly != null)
                                return assembly;
+#endif
 
-                       throw new FileNotFoundException ("Could not resolve: " + name);
-               }
+                       if (ResolveFailure != null) {
+                               assembly = ResolveFailure (this, name);
+                               if (assembly != null)
+                                       return assembly;
+                       }
 
-               static readonly string [] _extentions = new string [] { ".dll", ".exe" };
+                       throw new AssemblyResolutionException (name);
+               }
 
-               static AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable directories)
+               AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable<string> directories, ReaderParameters parameters)
                {
-                       foreach (string dir in directories) {
-                               foreach (string ext in _extentions) {
-                                       string file = Path.Combine (dir, name.Name + ext);
+                       var extensions = new [] { ".exe", ".dll" };
+                       foreach (var directory in directories) {
+                               foreach (var extension in extensions) {
+                                       string file = Path.Combine (directory, name.Name + extension);
                                        if (File.Exists (file))
-                                               return AssemblyFactory.GetAssembly (file);
+                                               return GetAssembly (file, parameters);
                                }
                        }
 
@@ -116,100 +198,165 @@ namespace Mono.Cecil {
 
                static bool IsZero (Version version)
                {
-                       return version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0;
+                       return version == null || (version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0);
                }
 
-#if !CF_1_0 && !CF_2_0
-               static AssemblyDefinition GetCorlib (AssemblyNameReference reference)
+#if !SILVERLIGHT && !CF
+               AssemblyDefinition GetCorlib (AssemblyNameReference reference, ReaderParameters parameters)
                {
-                       SR.AssemblyName corlib = typeof (object).Assembly.GetName ();
-                       if (corlib.Version == reference.Version || IsZero (reference.Version))
-                               return AssemblyFactory.GetAssembly (typeof (object).Module.FullyQualifiedName);
+                       var version = reference.Version;
+                       var corlib = typeof (object).Assembly.GetName ();
 
-                       string path = Directory.GetParent (
+                       if (corlib.Version == version || IsZero (version))
+                               return GetAssembly (typeof (object).Module.FullyQualifiedName, parameters);
+
+                       var path = Directory.GetParent (
                                Directory.GetParent (
                                        typeof (object).Module.FullyQualifiedName).FullName
                                ).FullName;
 
-                       if (OnMono ()) {
-                               if (reference.Version.Major == 1)
+                       if (on_mono) {
+                               if (version.Major == 1)
                                        path = Path.Combine (path, "1.0");
-                               else if (reference.Version.Major == 2) {
-                                       if (reference.Version.Minor == 1)
+                               else if (version.Major == 2) {
+                                       if (version.MajorRevision == 5)
                                                path = Path.Combine (path, "2.1");
                                        else
                                                path = Path.Combine (path, "2.0");
-                               } else
-                                       throw new NotSupportedException ("Version not supported: " + reference.Version);
+                               } else if (version.Major == 4)
+                                       path = Path.Combine (path, "4.0");
+                               else
+                                       throw new NotSupportedException ("Version not supported: " + version);
                        } else {
-                               if (reference.Version.ToString () == "1.0.3300.0")
-                                       path = Path.Combine (path, "v1.0.3705");
-                               else if (reference.Version.ToString () == "1.0.5000.0")
-                                       path = Path.Combine (path, "v1.1.4322");
-                               else if (reference.Version.ToString () == "2.0.0.0")
+                               switch (version.Major) {
+                               case 1:
+                                       if (version.MajorRevision == 3300)
+                                               path = Path.Combine (path, "v1.0.3705");
+                                       else
+                                               path = Path.Combine (path, "v1.0.5000.0");
+                                       break;
+                               case 2:
                                        path = Path.Combine (path, "v2.0.50727");
-                               else if (reference.Version.ToString () == "4.0.0.0")
-                                       path = Path.Combine (path, "v4.0.20506");
-                               else
-                                       throw new NotSupportedException ("Version not supported: " + reference.Version);
+                                       break;
+                               case 4:
+                                       path = Path.Combine (path, "v4.0.30319");
+                                       break;
+                               default:
+                                       throw new NotSupportedException ("Version not supported: " + version);
+                               }
                        }
 
-                       if (File.Exists (Path.Combine (path, "mscorlib.dll")))
-                               return AssemblyFactory.GetAssembly (Path.Combine (path, "mscorlib.dll"));
+                       var file = Path.Combine (path, "mscorlib.dll");
+                       if (File.Exists (file))
+                               return GetAssembly (file, parameters);
 
                        return null;
                }
 
-               public static bool OnMono ()
+               static Collection<string> GetGacPaths ()
+               {
+                       if (on_mono)
+                               return GetDefaultMonoGacPaths ();
+
+                       var paths = new Collection<string> (2);
+                       var windir = Environment.GetEnvironmentVariable ("WINDIR");
+                       if (windir == null)
+                               return paths;
+
+                       paths.Add (Path.Combine (windir, "assembly"));
+                       paths.Add (Path.Combine (windir, Path.Combine ("Microsoft.NET", "assembly")));
+                       return paths;
+               }
+
+               static Collection<string> GetDefaultMonoGacPaths ()
+               {
+                       var paths = new Collection<string> (1);
+                       var gac = GetCurrentMonoGac ();
+                       if (gac != null)
+                               paths.Add (gac);
+
+                       var gac_paths_env = Environment.GetEnvironmentVariable ("MONO_GAC_PREFIX");
+                       if (string.IsNullOrEmpty (gac_paths_env))
+                               return paths;
+
+                       var prefixes = gac_paths_env.Split (Path.PathSeparator);
+                       foreach (var prefix in prefixes) {
+                               if (string.IsNullOrEmpty (prefix))
+                                       continue;
+
+                               var gac_path = Path.Combine (Path.Combine (Path.Combine (prefix, "lib"), "mono"), "gac");
+                               if (Directory.Exists (gac_path) && !paths.Contains (gac))
+                                       paths.Add (gac_path);
+                       }
+
+                       return paths;
+               }
+
+               static string GetCurrentMonoGac ()
                {
-                       return typeof (object).Assembly.GetType ("System.MonoType", false) != null;
+                       return Path.Combine (
+                               Directory.GetParent (
+                                       Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName)).FullName,
+                               "gac");
                }
 
-               static AssemblyDefinition GetAssemblyInGac (AssemblyNameReference reference)
+               AssemblyDefinition GetAssemblyInGac (AssemblyNameReference reference, ReaderParameters parameters)
                {
                        if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0)
                                return null;
 
-                       string currentGac = GetCurrentGacPath ();
-                       if (OnMono ()) {
-                               string s = GetAssemblyFile (reference, currentGac);
-                               if (File.Exists (s))
-                                       return AssemblyFactory.GetAssembly (s);
-                       } else {
-                               string [] gacs = new string [] {"GAC_MSIL", "GAC_32", "GAC"};
-                               for (int i = 0; i < gacs.Length; i++) {
-                                       string gac = Path.Combine (Directory.GetParent (currentGac).FullName, gacs [i]);
-                                       string asm = GetAssemblyFile (reference, gac);
-                                       if (Directory.Exists (gac) && File.Exists (asm))
-                                               return AssemblyFactory.GetAssembly (asm);
+                       if (gac_paths == null)
+                               gac_paths = GetGacPaths ();
+
+                       if (on_mono)
+                               return GetAssemblyInMonoGac (reference, parameters);
+
+                       return GetAssemblyInNetGac (reference, parameters);
+               }
+
+               AssemblyDefinition GetAssemblyInMonoGac (AssemblyNameReference reference, ReaderParameters parameters)
+               {
+                       for (int i = 0; i < gac_paths.Count; i++) {
+                               var gac_path = gac_paths [i];
+                               var file = GetAssemblyFile (reference, string.Empty, gac_path);
+                               if (File.Exists (file))
+                                       return GetAssembly (file, parameters);
+                       }
+
+                       return null;
+               }
+
+               AssemblyDefinition GetAssemblyInNetGac (AssemblyNameReference reference, ReaderParameters parameters)
+               {
+                       var gacs = new [] { "GAC_MSIL", "GAC_32", "GAC" };
+                       var prefixes = new [] { string.Empty, "v4.0_" };
+
+                       for (int i = 0; i < 2; i++) {
+                               for (int j = 0; j < gacs.Length; j++) {
+                                       var gac = Path.Combine (gac_paths [i], gacs [j]);
+                                       var file = GetAssemblyFile (reference, prefixes [i], gac);
+                                       if (Directory.Exists (gac) && File.Exists (file))
+                                               return GetAssembly (file, parameters);
                                }
                        }
 
                        return null;
                }
 
-               static string GetAssemblyFile (AssemblyNameReference reference, string gac)
+               static string GetAssemblyFile (AssemblyNameReference reference, string prefix, string gac)
                {
-                       StringBuilder sb = new StringBuilder ();
-                       sb.Append (reference.Version);
-                       sb.Append ("__");
+                       var gac_folder = new StringBuilder ()
+                               .Append (prefix)
+                               .Append (reference.Version)
+                               .Append ("__");
+
                        for (int i = 0; i < reference.PublicKeyToken.Length; i++)
-                               sb.Append (reference.PublicKeyToken [i].ToString ("x2"));
+                               gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2"));
 
                        return Path.Combine (
                                Path.Combine (
-                                       Path.Combine (gac, reference.Name), sb.ToString ()),
-                                       string.Concat (reference.Name, ".dll"));
-               }
-
-               static string GetCurrentGacPath ()
-               {
-                       return Directory.GetParent (
-                               Directory.GetParent (
-                                       Path.GetDirectoryName (
-                                               typeof (Uri).Module.FullyQualifiedName)
-                                       ).FullName
-                               ).FullName;
+                                       Path.Combine (gac, reference.Name), gac_folder.ToString ()),
+                               reference.Name + ".dll");
                }
 #endif
        }