Skip to content

Commit

Permalink
Now tracking all assemblies for forwarded/exported types.
Browse files Browse the repository at this point in the history
Resolves #342
  • Loading branch information
joelmartinez committed Sep 11, 2018
1 parent 608ded7 commit bfd47cb
Show file tree
Hide file tree
Showing 11 changed files with 260 additions and 17 deletions.
35 changes: 34 additions & 1 deletion mdoc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,17 @@ Test/DocTest-enumerations.dll:
Test/DocTest-embedded-type.dll:
$(CSCOMPILE) $(TEST_CSCFLAGS) -unsafe -debug -optimize -target:library -out:$@ Test/DocTest-embedded-type.cs

Test/DocTest-typeForwards-First.dll:
rm -f $@
$(CSCOMPILE) $(TEST_CSCFLAGS) -unsafe -debug -optimize -target:library -out:$@ Test/DocTest-typeForwards.cs /define:FIRST

.PHONY: Test/DocTest-typeForwards-Second.dll
Test/DocTest-typeForwards-Second.dll:
rm -f $@
rm -f Test/DocTest-typeForwards-Second-First.dll
$(CSCOMPILE) $(TEST_CSCFLAGS) -unsafe -debug -optimize -target:library -out:$@ Test/DocTest-typeForwards.cs /define:FIRST
$(CSCOMPILE) $(TEST_CSCFLAGS) -unsafe -debug -optimize -target:library -out:Test/DocTest-typeForwards-Second-First.dll /reference:$@ Test/DocTest-typeForwards.cs /define:SECOND

.PHONY: Test/FrameworkTestData
Test/FrameworkTestData: Test/DocTest-addNonGeneric.dll Test/DocTest-DropNS-classic.dll Test/DocTest-DropNS-classic-secondary.dll
rm -rf Test/FrameworkTestData
Expand All @@ -155,6 +166,26 @@ Test/FrameworkTestData-fx-inheritance: Test/DocTest-framework-inheritance-one.dl
cp Test/DocTest-framework-inheritance-two.dll Test/FrameworkTestData-fx-inheritance/Two/
$(MONO) $(PROGRAM) fx-bootstrap Test/FrameworkTestData-fx-inheritance

.PHONY: check-monodocer-typeForwards
check-monodocer-typeForwards : Test/DocTest-typeForwards-First.dll Test/DocTest-typeForwards-Second.dll
-rm -Rf Test/en.actual

# set up the fx test data
-rm -Rf Test/FrameworkTestData-fx-typeForwards
mkdir Test/FrameworkTestData-fx-typeForwards
mkdir Test/FrameworkTestData-fx-typeForwards/One
mkdir Test/FrameworkTestData-fx-typeForwards/Two
mkdir Test/FrameworkTestData-fx-typeForwards/dependencies
mkdir Test/FrameworkTestData-fx-typeForwards/dependencies/Two
cp Test/DocTest-typeForwards-First.dll Test/FrameworkTestData-fx-typeForwards/One
cp Test/DocTest-typeForwards-Second-First.dll Test/FrameworkTestData-fx-typeForwards/Two
cp Test/DocTest-typeForwards-Second.dll Test/FrameworkTestData-fx-typeForwards/dependencies/Two
$(MONO) $(PROGRAM) fx-bootstrap Test/FrameworkTestData-fx-typeForwards

# now run mdoc update
$(MONO) $(PROGRAM) update -o Test/en.actual -frameworks Test/FrameworkTestData-fx-typeForwards
$(DIFF) Test/en.expected.typeForwards Test/en.actual

check-monodocer-frameworks: Test/FrameworkTestData
-rm -Rf Test/en.actual
$(MONO) $(PROGRAM) update -o Test/en.actual -frameworks Test/FrameworkTestData
Expand Down Expand Up @@ -708,7 +739,9 @@ run-test-local: check-doc-tools

run-test-update : check-doc-tools-update

check-doc-tools: check-monodocer-Eii-importslashdoc \
check-doc-tools: \
check-monodocer-typeForwards \
check-monodocer-Eii-importslashdoc \
check-monodocer-Eii-importecmadoc-oldNames \
check-monodocer-Eii \
check-monodocer-since \
Expand Down
45 changes: 34 additions & 11 deletions mdoc/Mono.Documentation/MDocUpdater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,12 @@ public static bool HasDroppedNamespace (AssemblyDefinition forAssembly)

public static bool HasDroppedNamespace (ModuleDefinition forModule)
{
return !string.IsNullOrWhiteSpace (droppedNamespace) && droppedAssemblies.Any (da => da == forModule.Name);
return HasDroppedNamespace (forModule.Assembly.Name);
}

public static bool HasDroppedNamespace (AssemblyNameReference assemblyRef)
{
return !string.IsNullOrWhiteSpace (droppedNamespace) && droppedAssemblies.Any (da => Path.GetFileNameWithoutExtension(da) == assemblyRef.Name);
}

public static bool HasDroppedAnyNamespace ()
Expand Down Expand Up @@ -2137,42 +2142,55 @@ public void UpdateType (XmlElement root, TypeDefinition type, FrameworkTypeEntry

/// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
/// <returns>The assembly that was either added, or was already present</returns>
XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type)
XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition forType)
{
return AddAssemblyNameToNode (root, type.Module);
return AddAssemblyNameToNode (root, forType.Module, forType);
}

XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module, TypeDefinition forType)
{
var list = this.assemblies.SelectMany (a => a.FullAssemblyChain (forType));

var names = list.Select(a => AddAssemblyNameToNodeCore (root, a, forType));
return names.ToArray().First ();
}

XmlElement AddAssemblyNameToNodeCore (XmlElement root, ModuleDefinition module, TypeDefinition forType)
{
return AddAssemblyNameToNodeCore (root, module.Assembly.Name, forType);
}

/// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
/// <returns>The assembly that was either added, or was already present</returns>
XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module)
XmlElement AddAssemblyNameToNodeCore (XmlElement root, AssemblyNameReference assembly, TypeDefinition forType)
{
Func<XmlElement, bool> assemblyFilter = x =>
{
var existingName = x.SelectSingleNode ("AssemblyName");

bool apiStyleMatches = true;
string currentApiStyle = x.GetAttribute ("apistyle");
if ((HasDroppedNamespace (module) && !string.IsNullOrWhiteSpace (currentApiStyle) && currentApiStyle != "unified") ||
if ((HasDroppedNamespace (assembly) && !string.IsNullOrWhiteSpace (currentApiStyle) && currentApiStyle != "unified") ||
(isClassicRun && (string.IsNullOrWhiteSpace (currentApiStyle) || currentApiStyle != "classic")))
{
apiStyleMatches = false;
}
return apiStyleMatches && (existingName == null || (existingName != null && existingName.InnerText == module.Assembly.Name.Name));
return apiStyleMatches && (existingName == null || (existingName != null && existingName.InnerText == assembly.Name));
};

return AddAssemblyXmlNode (
root.SelectNodes ("AssemblyInfo").Cast<XmlElement> ().ToArray (),
assemblyFilter, x => WriteElementText (x, "AssemblyName", module.Assembly.Name.Name),
assemblyFilter, x => WriteElementText (x, "AssemblyName", assembly.Name),
() =>
{
XmlElement ass = WriteElement (root, "AssemblyInfo", forceNewElement: true);

if (MDocUpdater.HasDroppedNamespace (module))
if (MDocUpdater.HasDroppedNamespace (assembly))
ass.AddApiStyle (ApiStyle.Unified);
if (isClassicRun)
ass.AddApiStyle (ApiStyle.Classic);
return ass;
}, module);
}, assembly);
}

static readonly string[] TypeNodeOrder = {
Expand Down Expand Up @@ -2223,7 +2241,7 @@ private void UpdateMember (DocsNodeInfo info, FrameworkTypeEntry typeEntry, Dict
UpdateAssemblyVersions (me, mi, true);
else
{
var node = AddAssemblyNameToNode (me, mi.Module);
var node = AddAssemblyNameToNode (me, mi.Module, mi.DeclaringType.Resolve());

UpdateAssemblyVersionForAssemblyInfo (node, me, new[] { GetAssemblyVersion (mi.Module.Assembly) }, add: true);
}
Expand Down Expand Up @@ -2477,7 +2495,12 @@ static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatch

static XmlElement AddAssemblyXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
{
bool isUnified = MDocUpdater.HasDroppedNamespace (module);
return AddAssemblyXmlNode (relevant, valueMatches, setValue, makeNewNode, module.Assembly.Name);
}

static XmlElement AddAssemblyXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, AssemblyNameReference assembly)
{
bool isUnified = MDocUpdater.HasDroppedNamespace (assembly);
XmlElement thisAssemblyNode = relevant.FirstOrDefault (valueMatches);
if (thisAssemblyNode == null)
{
Expand Down
59 changes: 57 additions & 2 deletions mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,19 @@ public IDictionary<string, bool> AssemblyMapsPath
get => assemblyPathsMap;
}

private IDictionary<string, List<MDocResolver.TypeForwardEventArgs>> forwardedTypesTo = new Dictionary<string, List<MDocResolver.TypeForwardEventArgs>> ();


public AssemblySet (IEnumerable<string> paths) : this ("Default", paths, new string[0]) { }

public AssemblySet (string name, IEnumerable<string> paths, IEnumerable<string> resolverSearchPaths, IEnumerable<string> imports = null, string version = null, string id = null)
{
cachedResolver = cachedResolver ?? new CachedResolver (resolver);
metadataResolver = metadataResolver ?? new Frameworks.MDocMetadataResolver (cachedResolver);
((MDocResolver)resolver).TypeExported += (sender, e) =>
{
TrackTypeExported (e);
};

Name = name;
Version = version;
Expand Down Expand Up @@ -83,6 +90,17 @@ public AssemblySet (string name, IEnumerable<string> paths, IEnumerable<string>
this.Importers = new DocumentationImporter[0];
}

private void TrackTypeExported (MDocResolver.TypeForwardEventArgs e)
{
// keep track of types that have been exported for this assemblyset
if (!forwardedTypesTo.ContainsKey (e.ForType.FullName))
{
forwardedTypesTo.Add (e.ForType.FullName, new List<MDocResolver.TypeForwardEventArgs> ());
}

forwardedTypesTo[e.ForType.FullName].Add (e);
}

public string Name { get; private set; }
public string Version { get; private set; }
public string Id { get; private set; }
Expand Down Expand Up @@ -133,6 +151,40 @@ public bool ContainsForwardedType (string name)
return forwardedTypes.Contains (name);
}

/// <summary>
/// Forwardeds the assemblies.
/// </summary>
/// <returns>The assemblies.</returns>
/// <param name="type">Type.</param>
public IEnumerable<AssemblyNameReference> FullAssemblyChain(TypeDefinition type)
{
if (forwardedTypesTo.ContainsKey (type.FullName))
{
var list = forwardedTypesTo[type.FullName];
var assemblies = (new[] { type.Module.Assembly.Name })
.Union (list.Select (f => f.To))
.Union (list.Select (f => f.From))
.Distinct (anc);
return assemblies;
}
else
return new[] { type.Module.Assembly.Name };
}

AssemblyNameComparer anc = new AssemblyNameComparer ();
class AssemblyNameComparer : IEqualityComparer<AssemblyNameReference>
{
public bool Equals (AssemblyNameReference x, AssemblyNameReference y)
{
return x.FullName.Equals (y.FullName, StringComparison.Ordinal);
}

public int GetHashCode (AssemblyNameReference obj)
{
return obj.FullName.GetHashCode ();
}
}

public void Dispose ()
{
this.assemblies = null;
Expand All @@ -149,8 +201,11 @@ IEnumerable<AssemblyDefinition> LoadAllAssemblies ()
foreach (var path in this.assemblyPaths) {
var assembly = MDocUpdater.Instance.LoadAssembly (path, metadataResolver, cachedResolver);
if (assembly != null) {
foreach (var type in assembly.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
forwardedTypes.Add (type);
foreach (var type in assembly.MainModule.ExportedTypes.Where (t => t.IsForwarder).Cast<ExportedType>())
{
forwardedTypes.Add (type.FullName);
TrackTypeExported (new MDocResolver.TypeForwardEventArgs (assembly.Name, (AssemblyNameReference)type.Scope, type.Resolve ()));
}
}
yield return assembly;
}
Expand Down
23 changes: 21 additions & 2 deletions mdoc/Mono.Documentation/Updater/Frameworks/MDocResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,29 @@ namespace Mono.Documentation.Updater.Frameworks
/// <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
public class MDocResolver : MDocBaseResolver
{
public class TypeForwardEventArgs : EventArgs
{
public AssemblyNameReference From { get; private set; }
public AssemblyNameReference To { get; private set; }
public TypeReference ForType { get; set; }

public TypeForwardEventArgs(AssemblyNameReference from, AssemblyNameReference to, TypeReference forType)
{
From = from;
To = to;
ForType = forType;
}
}

public override AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
{
return Resolve (name, parameters, null, null);
}

public event EventHandler<TypeForwardEventArgs> TypeExported;

internal AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters r, TypeReference forType, List<string> exportedFiles)
{
if (exportedFiles == null)
Expand All @@ -39,6 +55,9 @@ internal AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameter
string file = a.MainModule.FileName;
AssemblyNameReference exportedTo = (AssemblyNameReference)etype.Scope;
Console.WriteLine ($"resolving {forType.FullName} in {name.FullName}. Found {file}, but it's exported to {exportedTo.FullName}");
if (forType != null)
TypeExported?.Invoke (this, new TypeForwardEventArgs(name, exportedTo, forType));

exportedFiles.Add (file);
return Resolve (exportedTo, r, forType, exportedFiles);
}
Expand Down Expand Up @@ -278,7 +297,7 @@ public void Dispose ()
/// <remarks>
/// There are two changes made from the original source
/// </remarks>
abstract class MDocBaseResolver : BaseAssemblyResolver
public abstract class MDocBaseResolver : BaseAssemblyResolver
{
static readonly bool on_mono = Type.GetType ("Mono.Runtime") != null;

Expand Down
3 changes: 2 additions & 1 deletion mdoc/Mono.Documentation/Util/CecilExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference

public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
{
return assembly.Modules.SelectMany (md => md.GetAllTypes ());
var types = assembly.Modules.SelectMany (md => md.GetAllTypes ()).Union(assembly.MainModule.ExportedTypes.Select(et => et.Resolve()));
return types;
}

public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
Expand Down
12 changes: 12 additions & 0 deletions mdoc/Test/DocTest-typeForwards.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#if SECOND
using System.Runtime.CompilerServices;
[assembly:TypeForwardedToAttribute(typeof(TheNamespace.TheClass))]
#endif

namespace TheNamespace
{
#if FIRST
public class TheClass
{}
#endif
}
8 changes: 8 additions & 0 deletions mdoc/Test/en.expected.typeForwards/FrameworksIndex/One.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Framework Name="One">
<Namespace Name="TheNamespace">
<Type Name="TheNamespace.TheClass" Id="T:TheNamespace.TheClass">
<Member Id="M:TheNamespace.TheClass.#ctor" />
</Type>
</Namespace>
</Framework>
8 changes: 8 additions & 0 deletions mdoc/Test/en.expected.typeForwards/FrameworksIndex/Two.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Framework Name="Two">
<Namespace Name="TheNamespace">
<Type Name="TheNamespace.TheClass" Id="T:TheNamespace.TheClass">
<Member Id="M:TheNamespace.TheClass.#ctor" />
</Type>
</Namespace>
</Framework>
46 changes: 46 additions & 0 deletions mdoc/Test/en.expected.typeForwards/TheNamespace/TheClass.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<Type Name="TheClass" FullName="TheNamespace.TheClass">
<TypeSignature Language="C#" Value="public class TheClass" />
<TypeSignature Language="ILAsm" Value=".class public auto ansi beforefieldinit TheClass extends System.Object" />
<AssemblyInfo>
<AssemblyName>DocTest-typeForwards-First</AssemblyName>
<AssemblyVersion>0.0.0.0</AssemblyVersion>
</AssemblyInfo>
<AssemblyInfo>
<AssemblyName>DocTest-typeForwards-Second</AssemblyName>
<AssemblyVersion>0.0.0.0</AssemblyVersion>
</AssemblyInfo>
<AssemblyInfo>
<AssemblyName>DocTest-typeForwards-Second-First</AssemblyName>
</AssemblyInfo>
<Base>
<BaseTypeName>System.Object</BaseTypeName>
</Base>
<Interfaces />
<Docs>
<summary>To be added.</summary>
<remarks>To be added.</remarks>
</Docs>
<Members>
<Member MemberName=".ctor">
<MemberSignature Language="C#" Value="public TheClass ();" />
<MemberSignature Language="ILAsm" Value=".method public hidebysig specialname rtspecialname instance void .ctor() cil managed" />
<MemberType>Constructor</MemberType>
<AssemblyInfo>
<AssemblyName>DocTest-typeForwards-First</AssemblyName>
<AssemblyVersion>0.0.0.0</AssemblyVersion>
</AssemblyInfo>
<AssemblyInfo>
<AssemblyName>DocTest-typeForwards-Second</AssemblyName>
<AssemblyVersion>0.0.0.0</AssemblyVersion>
</AssemblyInfo>
<AssemblyInfo>
<AssemblyName>DocTest-typeForwards-Second-First</AssemblyName>
</AssemblyInfo>
<Parameters />
<Docs>
<summary>To be added.</summary>
<remarks>To be added.</remarks>
</Docs>
</Member>
</Members>
</Type>
Loading

0 comments on commit bfd47cb

Please sign in to comment.