371 lines
10 KiB
C#
371 lines
10 KiB
C#
|
//
|
||
|
// BaseAssemblyResolver.cs
|
||
|
//
|
||
|
// Author:
|
||
|
// Jb Evain (jbevain@gmail.com)
|
||
|
//
|
||
|
// 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
|
||
|
// "Software"), to deal in the Software without restriction, including
|
||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||
|
// the following conditions:
|
||
|
//
|
||
|
// The above copyright notice and this permission notice shall be
|
||
|
// included in all copies or substantial portions of the Software.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||
|
// 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 {
|
||
|
|
||
|
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 {
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
directories.Add (directory);
|
||
|
}
|
||
|
|
||
|
public void RemoveSearchDirectory (string directory)
|
||
|
{
|
||
|
directories.Remove (directory);
|
||
|
}
|
||
|
|
||
|
public string [] GetSearchDirectories ()
|
||
|
{
|
||
|
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 (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" };
|
||
|
}
|
||
|
|
||
|
AssemblyDefinition GetAssembly (string file, ReaderParameters parameters)
|
||
|
{
|
||
|
if (parameters.AssemblyResolver == null)
|
||
|
parameters.AssemblyResolver = this;
|
||
|
|
||
|
return ModuleDefinition.ReadModule (file, parameters).Assembly;
|
||
|
}
|
||
|
|
||
|
public virtual AssemblyDefinition Resolve (AssemblyNameReference name)
|
||
|
{
|
||
|
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 ();
|
||
|
|
||
|
var assembly = SearchDirectory (name, directories, parameters);
|
||
|
if (assembly != null)
|
||
|
return assembly;
|
||
|
|
||
|
#if !SILVERLIGHT && !CF
|
||
|
if (name.IsRetargetable) {
|
||
|
// if the reference is retargetable, zero it
|
||
|
name = new AssemblyNameReference (name.Name, new Version (0, 0, 0, 0)) {
|
||
|
PublicKeyToken = Empty<byte>.Array,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName);
|
||
|
|
||
|
if (IsZero (name.Version)) {
|
||
|
assembly = SearchDirectory (name, new [] { framework_dir }, parameters);
|
||
|
if (assembly != null)
|
||
|
return assembly;
|
||
|
}
|
||
|
|
||
|
if (name.Name == "mscorlib") {
|
||
|
assembly = GetCorlib (name, parameters);
|
||
|
if (assembly != null)
|
||
|
return assembly;
|
||
|
}
|
||
|
|
||
|
assembly = GetAssemblyInGac (name, parameters);
|
||
|
if (assembly != null)
|
||
|
return assembly;
|
||
|
|
||
|
assembly = SearchDirectory (name, new [] { framework_dir }, parameters);
|
||
|
if (assembly != null)
|
||
|
return assembly;
|
||
|
#endif
|
||
|
|
||
|
if (ResolveFailure != null) {
|
||
|
assembly = ResolveFailure (this, name);
|
||
|
if (assembly != null)
|
||
|
return assembly;
|
||
|
}
|
||
|
|
||
|
throw new AssemblyResolutionException (name);
|
||
|
}
|
||
|
|
||
|
AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable<string> directories, ReaderParameters parameters)
|
||
|
{
|
||
|
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 GetAssembly (file, parameters);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
static bool IsZero (Version version)
|
||
|
{
|
||
|
return version == null || (version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0);
|
||
|
}
|
||
|
|
||
|
#if !SILVERLIGHT && !CF
|
||
|
AssemblyDefinition GetCorlib (AssemblyNameReference reference, ReaderParameters parameters)
|
||
|
{
|
||
|
var version = reference.Version;
|
||
|
var corlib = typeof (object).Assembly.GetName ();
|
||
|
|
||
|
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 (on_mono) {
|
||
|
if (version.Major == 1)
|
||
|
path = Path.Combine (path, "1.0");
|
||
|
else if (version.Major == 2) {
|
||
|
if (version.MajorRevision == 5)
|
||
|
path = Path.Combine (path, "2.1");
|
||
|
else
|
||
|
path = Path.Combine (path, "2.0");
|
||
|
} else if (version.Major == 4)
|
||
|
path = Path.Combine (path, "4.0");
|
||
|
else
|
||
|
throw new NotSupportedException ("Version not supported: " + version);
|
||
|
} else {
|
||
|
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");
|
||
|
break;
|
||
|
case 4:
|
||
|
path = Path.Combine (path, "v4.0.30319");
|
||
|
break;
|
||
|
default:
|
||
|
throw new NotSupportedException ("Version not supported: " + version);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var file = Path.Combine (path, "mscorlib.dll");
|
||
|
if (File.Exists (file))
|
||
|
return GetAssembly (file, parameters);
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
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 Path.Combine (
|
||
|
Directory.GetParent (
|
||
|
Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName)).FullName,
|
||
|
"gac");
|
||
|
}
|
||
|
|
||
|
AssemblyDefinition GetAssemblyInGac (AssemblyNameReference reference, ReaderParameters parameters)
|
||
|
{
|
||
|
if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0)
|
||
|
return null;
|
||
|
|
||
|
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 prefix, string gac)
|
||
|
{
|
||
|
var gac_folder = new StringBuilder ()
|
||
|
.Append (prefix)
|
||
|
.Append (reference.Version)
|
||
|
.Append ("__");
|
||
|
|
||
|
for (int i = 0; i < reference.PublicKeyToken.Length; i++)
|
||
|
gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2"));
|
||
|
|
||
|
return Path.Combine (
|
||
|
Path.Combine (
|
||
|
Path.Combine (gac, reference.Name), gac_folder.ToString ()),
|
||
|
reference.Name + ".dll");
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|