Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/api-doc-tools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Martinez <joelmartinez@gmail.com>2017-10-20 18:10:11 +0300
committerJoel Martinez <joelmartinez@gmail.com>2017-10-20 18:27:18 +0300
commit518a7b04e855dbe25ecdfcb34a77d6f736faef0c (patch)
tree30d988fafdbd4cf07f66259921f3d1a3e90babab
parentfa6665913ddc320f8baba9ff91cac52d32442880 (diff)
mdoc: New resolver now takes 'version' into account when loading.
This closes #134
-rw-r--r--mdoc/Mono.Documentation/MDocUpdater.cs1
-rw-r--r--mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs10
-rw-r--r--mdoc/Mono.Documentation/Updater/Frameworks/MDocResolver.cs460
-rw-r--r--mdoc/Mono.Documentation/Updater/Frameworks/UwpResolver.cs25
-rw-r--r--mdoc/Mono.Documentation/exceptions.cs7
-rw-r--r--mdoc/mdoc.csproj2
6 files changed, 472 insertions, 33 deletions
diff --git a/mdoc/Mono.Documentation/MDocUpdater.cs b/mdoc/Mono.Documentation/MDocUpdater.cs
index cab63bed..d789e9af 100644
--- a/mdoc/Mono.Documentation/MDocUpdater.cs
+++ b/mdoc/Mono.Documentation/MDocUpdater.cs
@@ -2161,7 +2161,6 @@ namespace Mono.Documentation
{
foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName))
{
-
TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
if (attrType != null && !IsPublic (attrType))
continue;
diff --git a/mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs b/mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs
index 524e39a7..28a46ce3 100644
--- a/mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs
+++ b/mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs
@@ -12,7 +12,8 @@ namespace Mono.Documentation.Updater.Frameworks
/// </summary>
class AssemblySet : IDisposable
{
- readonly DefaultAssemblyResolver resolver = new Frameworks.UwpResolver ();
+ readonly BaseAssemblyResolver resolver = new Frameworks.MDocResolver ();
+ IAssemblyResolver cachedResolver;
HashSet<string> assemblyPaths = new HashSet<string> ();
HashSet<string> assemblySearchPaths = new HashSet<string> ();
HashSet<string> forwardedTypes = new HashSet<string> ();
@@ -23,6 +24,8 @@ namespace Mono.Documentation.Updater.Frameworks
public AssemblySet (string name, IEnumerable<string> paths, IEnumerable<string> resolverSearchPaths, IEnumerable<string> imports)
{
+ this.cachedResolver = new CachedResolver (this.resolver);
+
Name = name;
foreach (var path in paths)
@@ -65,6 +68,7 @@ namespace Mono.Documentation.Updater.Frameworks
.Select(d => new DirectoryInfo (d))
.Where (d => d.Exists)
.Select(d => d.FullName)
+ .Distinct ()
.ToDictionary(d => d, d => d);
var subdirs = directories.Keys
@@ -89,7 +93,7 @@ namespace Mono.Documentation.Updater.Frameworks
return forwardedTypes.Contains (name);
}
- public void Dispose () => resolver.Dispose ();
+ public void Dispose () => cachedResolver.Dispose ();
public override string ToString ()
{
@@ -99,7 +103,7 @@ namespace Mono.Documentation.Updater.Frameworks
IEnumerable<AssemblyDefinition> LoadAllAssemblies ()
{
foreach (var path in this.assemblyPaths) {
- var assembly = MDocUpdater.Instance.LoadAssembly (path, this.resolver);
+ var assembly = MDocUpdater.Instance.LoadAssembly (path, this.cachedResolver);
if (assembly != null) {
foreach (var type in assembly.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
forwardedTypes.Add (type);
diff --git a/mdoc/Mono.Documentation/Updater/Frameworks/MDocResolver.cs b/mdoc/Mono.Documentation/Updater/Frameworks/MDocResolver.cs
new file mode 100644
index 00000000..16e468c8
--- /dev/null
+++ b/mdoc/Mono.Documentation/Updater/Frameworks/MDocResolver.cs
@@ -0,0 +1,460 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Mono.Cecil;
+using Mono.Collections.Generic;
+
+namespace Mono.Documentation.Updater.Frameworks
+{
+ /// <summary>resolver to handle assembly lookups</summary>
+ /// <remarks><para>This resolver handles two scenarios. First is UWP
+ /// references to 'mscorlib' (which simply picks up the 4.5 version,
+ /// or whatever you have installed). And second, it tries its
+ /// best to match on version. If it can't find the exact version, it
+ /// will try to find the next highest version, and if that fails,
+ /// a lower version if available.</para>
+ /// <para>Please note that you will need to provide a reference
+ /// to the UWP framework directory, if you are trying to document
+ /// a UWP library. </para></remarks>
+ class MDocResolver : MDocBaseResolver
+ {
+ IEnumerable<string> fxpaths;
+ IEnumerable<string> gacpaths;
+
+ public override AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
+ {
+ var ver = name.Version;
+ if (ver.Major == 255 && ver.Minor == 255 && ver.Revision == 255 && name.Name == "mscorlib")
+ {
+ var v = new Version (4, 5, 0);
+ var anr = new AssemblyNameReference (name.Name, v);
+ return base.Resolve (anr, parameters);
+ }
+ else
+ return base.Resolve (name, parameters);
+ }
+
+ protected override AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable<string> directories, ReaderParameters parameters)
+ {
+ // look for an assembly that matches the name in all the search directories
+ string[] extensions = new[] { ".dll", ".exe", ".winmd" };
+
+ // Add the additional lookup directories
+ if (fxpaths == null) fxpaths = GetFrameworkPaths ().ToArray ();
+ if (gacpaths == null) gacpaths = GetGacPaths ().ToArray ();
+ directories = directories
+ .Concat (fxpaths)
+ .Concat (gacpaths);
+
+ var npaths = directories
+ .SelectMany (d => extensions.Select (e => Path.Combine (d, name.Name + e)))
+ .Distinct ();
+ var namedPaths = npaths
+ .Where (f => File.Exists (f));
+
+ if (!namedPaths.Any ()) return null;
+
+ Func<Version, int> aggregateVersion = version => version.Major * 100000 +
+ version.Minor * 10000 +
+ version.Build * 10 +
+ version.Revision;
+
+ Func<string, AssemblyDefinition> getAssemblies = (path) => {
+ return GetAssembly (path, parameters);
+ };
+
+ var applicableVersions = namedPaths.Select (getAssemblies)
+ .Select (a => new
+ {
+ Assembly = a,
+ VersionSort = aggregateVersion (a.Name.Version),
+ VersionDiff = aggregateVersion (a.Name.Version) - aggregateVersion (name.Version),
+ MajorMatches = a.Name.Version.Major == name.Version.Major
+ })
+ .ToArray ();
+
+ // Perfect Match
+ if (applicableVersions.Any (v => v.VersionDiff == 0))
+ return applicableVersions
+ .Where (v => v.VersionDiff == 0)
+ .Select (v => v.Assembly)
+ .Single ();
+
+ // closest high version
+ var newerVersions = applicableVersions
+ .Where (v => v.VersionDiff > 0)
+ .OrderBy (a => a.VersionSort)
+ .Select (v => v.Assembly)
+ .ToArray ();
+ if (newerVersions.Any ())
+ return newerVersions.First ();
+
+ // Are there any lower versions as a last resort?
+ var olderVersions = applicableVersions
+ .Where (v => v.VersionDiff < 0)
+ .OrderByDescending (v => v.VersionSort)
+ .Select (v => v.Assembly)
+ .ToArray ();
+ if (olderVersions.Any ())
+ return olderVersions.First ();
+
+ // return null if you don't find anything
+ return null;
+
+ }
+ }
+
+ /// <summary>
+ /// Wraps a resolver, and caches assemblies to avoid excessive calls to disk
+ /// </summary>
+ /// <remarks>
+ /// Inspired by Mono.Cecil's DefaultAssemblyResolver (<see href="https://github.com/jbevain/cecil/blob/master/Mono.Cecil/DefaultAssemblyResolver.cs"/>).
+ /// The big difference with this implementation, is one of using composition over inheritance.
+ /// </remarks>
+ class CachedResolver : IAssemblyResolver
+ {
+ readonly IDictionary<string, AssemblyDefinition> cache;
+
+ public IAssemblyResolver Resolver { get; private set; }
+
+ public CachedResolver (IAssemblyResolver resolver)
+ {
+ Resolver = resolver;
+ cache = new Dictionary<string, AssemblyDefinition> (StringComparer.Ordinal);
+ }
+
+ public AssemblyDefinition Resolve (AssemblyNameReference name)
+ {
+ return Resolve (name, new ReaderParameters () { AssemblyResolver = this });
+ }
+
+
+ public AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
+ {
+
+ AssemblyDefinition assembly;
+ if (cache.TryGetValue (name.FullName, out assembly))
+ return assembly;
+
+ assembly = this.Resolver.Resolve (name, parameters);
+ cache[name.FullName] = assembly;
+
+ return assembly;
+ }
+
+ protected void RegisterAssembly (AssemblyDefinition assembly)
+ {
+ if (assembly == null)
+ throw new ArgumentNullException (nameof(assembly));
+
+ var name = assembly.Name.FullName;
+ if (cache.ContainsKey (name))
+ return;
+
+ cache[name] = assembly;
+ }
+
+ bool disposed = false;
+ public void Dispose ()
+ {
+ if (disposed) return;
+
+ foreach (var assembly in cache.Values)
+ assembly.Dispose ();
+
+ cache.Clear ();
+
+ Resolver.Dispose ();
+
+ disposed = true;
+ }
+
+ }
+
+ /// <summary>
+ /// Sourced from Mono.Cecil's BaseAssemblyResolver: <see href="https://github.com/jbevain/cecil/blob/master/Mono.Cecil/BaseAssemblyResolver.cs" />
+ /// </summary>
+ /// <remarks>
+ /// There are two changes made from the original source
+ /// </remarks>
+ abstract class MDocBaseResolver : BaseAssemblyResolver
+ {
+ static readonly bool on_mono = Type.GetType ("Mono.Runtime") != null;
+
+ Collection<string> gac_paths;
+
+ protected AssemblyDefinition GetAssembly (string file, ReaderParameters parameters)
+ {
+ if (parameters.AssemblyResolver == null)
+ parameters.AssemblyResolver = this;
+
+ return ModuleDefinition.ReadModule (file, parameters).Assembly;
+ }
+
+ public override AssemblyDefinition Resolve (AssemblyNameReference name)
+ {
+ return this.Resolve (name, new ReaderParameters ());
+ }
+
+ public override AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
+ {
+ var directories = this.GetSearchDirectories ();
+ var assembly = SearchDirectory (name, directories, parameters);
+ if (assembly != null)
+ return assembly;
+
+ if (name.IsRetargetable)
+ {
+ // if the reference is retargetable, zero it
+ name = new AssemblyNameReference (name.Name, MDocResolverMixin.ZeroVersion)
+ {
+ PublicKeyToken = Empty<byte>.Array,
+ };
+ }
+
+ string[] framework_dirs = GetFrameworkPaths ();
+
+ if (IsZero (name.Version))
+ {
+ assembly = SearchDirectory (name, framework_dirs, 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, framework_dirs, parameters);
+ if (assembly != null)
+ return assembly;
+
+ throw new AssemblyResolutionException (name);
+ }
+
+ protected virtual AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable<string> directories, ReaderParameters parameters)
+ {
+ var extensions = name.IsWindowsRuntime ? new[] { ".winmd", ".dll" } : 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))
+ continue;
+ try
+ {
+ return GetAssembly (file, parameters);
+ }
+ catch (System.BadImageFormatException)
+ {
+ continue;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ static bool IsZero (Version version)
+ {
+ return version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0;
+ }
+
+ 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;
+ }
+
+ protected 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;
+ }
+
+ protected static string[] GetFrameworkPaths ()
+ {
+ var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName);
+ var framework_dirs = on_mono
+ ? new[] { framework_dir, Path.Combine (framework_dir, "Facades") }
+ : new[] { framework_dir };
+ return framework_dirs;
+ }
+
+ 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_64", "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");
+ }
+
+ // some helper classes
+ static class Empty<T>
+ {
+ public static readonly T[] Array = new T[0];
+ }
+
+ class MDocResolverMixin
+ {
+ public static Version ZeroVersion = new Version (0, 0, 0, 0);
+ }
+ }
+}
diff --git a/mdoc/Mono.Documentation/Updater/Frameworks/UwpResolver.cs b/mdoc/Mono.Documentation/Updater/Frameworks/UwpResolver.cs
deleted file mode 100644
index 4dbbec82..00000000
--- a/mdoc/Mono.Documentation/Updater/Frameworks/UwpResolver.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using Microsoft.Win32;
-using Mono.Cecil;
-
-namespace Mono.Documentation.Updater.Frameworks
-{
- /// <summary>Mono.Cecil resolver for the windows universal platform</summary>
- class UwpResolver : DefaultAssemblyResolver
- {
- public override AssemblyDefinition Resolve (AssemblyNameReference name)
- {
- var ver = name.Version;
- if (ver.Major == 255 && ver.Minor == 255 && ver.Revision == 255 && name.Name == "mscorlib")
- {
- var v = new Version (4, 5, 0);
- var anr = new AssemblyNameReference (name.Name, v);
- return base.Resolve (anr);
- }
- else
- return base.Resolve (name);
- }
- }
-} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/exceptions.cs b/mdoc/Mono.Documentation/exceptions.cs
index 47738499..bc2fce6e 100644
--- a/mdoc/Mono.Documentation/exceptions.cs
+++ b/mdoc/Mono.Documentation/exceptions.cs
@@ -85,9 +85,10 @@ namespace Mono.Documentation {
// so we can't do anything further.
return new ExceptionSources[0];
}
- throw new NotSupportedException (string.Format (
- "Unable to resolve member {0}::{1}.",
- member.DeclaringType.FullName, member.Name));
+ return new ExceptionSources[0];
+ //throw new NotSupportedException (string.Format (
+ // "Unable to resolve member {0}::{1}.",
+ // member.DeclaringType.FullName, member.Name));
}
string memberDecl = xdoc.GetDeclaration (member);
Dictionary<string, ExceptionSources> e;
diff --git a/mdoc/mdoc.csproj b/mdoc/mdoc.csproj
index 8bc16065..a1b2ea59 100644
--- a/mdoc/mdoc.csproj
+++ b/mdoc/mdoc.csproj
@@ -104,7 +104,7 @@
<Compile Include="Mono.Documentation\Updater\Frameworks\FrameworkEntry.cs" />
<Compile Include="Mono.Documentation\Updater\Frameworks\FrameworkTypeEntry.cs" />
<Compile Include="Mono.Documentation\Updater\Frameworks\AssemblySet.cs" />
- <Compile Include="Mono.Documentation\Updater\Frameworks\UwpResolver.cs" />
+ <Compile Include="Mono.Documentation\Updater\Frameworks\MDocResolver.cs" />
<Compile Include="Mono.Documentation\MDocUpdater.cs" />
</ItemGroup>
<ItemGroup>