[nnyeah] Move to generating map on the fly (#14868)

Remove the XML serialization and now make the map into a form that will work much better for actually modifying the assemblies
This commit is contained in:
Steve Hawley 2022-05-03 09:40:51 -04:00 коммит произвёл GitHub
Родитель b72e7be297
Коммит 1c7d63f393
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 60 добавлений и 161 удалений

Просмотреть файл

@ -36,7 +36,7 @@ namespace Microsoft.MaciOS.Nnyeah.AssemblyComparator {
continue;
}
TypeEvents.InvokeFound (this, typeNameAndValue.Key, laterElems.DeclaringType.ToString ());
TypeEvents.InvokeFound (this, typeNameAndValue.Key, laterElems.DeclaringType);
VisitAllMembers (reworker, typeNameAndValue.Value, laterElems);
}
@ -63,7 +63,7 @@ namespace Microsoft.MaciOS.Nnyeah.AssemblyComparator {
{
foreach (var late in later) {
if (elem.Signature == late.Signature) {
events.InvokeFound (this, elem.Signature, late.Signature);
events.InvokeFound (this, elem.Signature, late.Element);
return;
}
}
@ -74,7 +74,7 @@ namespace Microsoft.MaciOS.Nnyeah.AssemblyComparator {
}
foreach (var late in later) {
if (remappedSig == late.Signature) {
events.InvokeFound (this, elem.Signature, late.Signature);
events.InvokeFound (this, elem.Signature, late.Element);
return;
}
}

Просмотреть файл

@ -1,4 +1,5 @@
using System;
using Mono.Cecil;
#nullable enable
@ -12,20 +13,20 @@ namespace Microsoft.MaciOS.Nnyeah.AssemblyComparator {
}
public class ItemFoundEventArgs<T> : EventArgs {
public ItemFoundEventArgs (string original, string mapped)
public ItemFoundEventArgs (string original, T mapped)
{
Original = original;
Mapped = mapped;
}
public string Original { get; init; }
public string Mapped { get; init; }
public T Mapped { get; init; }
}
public class ItemEvents<T> {
public class ItemEvents<T> where T: IMemberDefinition {
public EventHandler<ItemNotFoundEventArgs<T>> NotFound = (s, e) => { };
public EventHandler<ItemFoundEventArgs<T>> Found = (s, e) => { };
internal void InvokeFound (object sender, string original, string mapped) =>
internal void InvokeFound (object sender, string original, T mapped) =>
Found.Invoke (sender, new (original, mapped));
internal void InvokeNotFound (object sender, string original) =>

Просмотреть файл

@ -1,5 +1,6 @@
using System.IO;
using System.Collections.Generic;
using Mono.Cecil;
using System.Xml.Serialization;
#nullable enable
@ -8,46 +9,22 @@ namespace Microsoft.MaciOS.Nnyeah.AssemblyComparator {
[XmlRoot]
public class TypeAndMemberMap {
public List<string> TypesNotPresent { get; init; } = new List<string> ();
public UniquingStringDictionary TypeMap { get; init; } = new UniquingStringDictionary ();
public Dictionary<string, TypeDefinition> TypeMap { get; init; } = new Dictionary<string, TypeDefinition> ();
public List<string> MethodsNotPresent { get; init; } = new List<string> ();
public UniquingStringDictionary MethodMap { get; init; } = new UniquingStringDictionary ();
public Dictionary<string, MethodDefinition> MethodMap { get; init; } = new Dictionary<string, MethodDefinition> ();
public List<string> FieldsNotPresent { get; init; } = new List<string> ();
public UniquingStringDictionary FieldMap { get; init; } = new UniquingStringDictionary ();
public Dictionary<string, FieldDefinition> FieldMap { get; init; } = new Dictionary<string, FieldDefinition> ();
public List<string> EventsNotPresent { get; init; } = new List<string> ();
public UniquingStringDictionary EventMap { get; init; } = new UniquingStringDictionary ();
public Dictionary<string, EventDefinition> EventMap { get; init; } = new Dictionary<string, EventDefinition> ();
public List<string> PropertiesNotPresent { get; init; } = new List<string> ();
public UniquingStringDictionary PropertyMap { get; init; } = new UniquingStringDictionary ();
public Dictionary<string, PropertyDefinition> PropertyMap { get; init; } = new Dictionary<string, PropertyDefinition> ();
public TypeAndMemberMap ()
{
}
public void ToXml (string path)
{
using var stm = new FileStream (path, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
ToXml (stm);
}
public void ToXml (Stream stm)
{
var serializer = new XmlSerializer (typeof (TypeAndMemberMap));
serializer.Serialize (stm, this);
}
public static TypeAndMemberMap? FromXml (string path)
{
using var stm = new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.Read);
return FromXml (stm);
}
public static TypeAndMemberMap? FromXml (Stream stm)
{
var serializer = new XmlSerializer (typeof (TypeAndMemberMap));
return (TypeAndMemberMap?)serializer.Deserialize (stm);
}
}
}

Просмотреть файл

@ -1,74 +0,0 @@
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
namespace Microsoft.MaciOS.Nnyeah.AssemblyComparator {
[XmlRoot ("dictionary")]
public class UniquingStringDictionary : Dictionary<string, string>, IXmlSerializable {
// if the key and the value are the same, we will serialized out the
// UnusedStringValue instead.
const string UnusedStringValue = "^";
const string kKey = "k";
const string kValue = "v";
const string kItem = "i";
public System.Xml.Schema.XmlSchema GetSchema ()
{
return null;
}
public void ReadXml (System.Xml.XmlReader reader)
{
XmlSerializer keySerializer = new XmlSerializer (typeof (string));
XmlSerializer valueSerializer = new XmlSerializer (typeof (string));
bool wasEmpty = reader.IsEmptyElement;
reader.Read ();
if (wasEmpty)
return;
while (reader.NodeType != System.Xml.XmlNodeType.EndElement) {
reader.ReadStartElement (kItem);
reader.ReadStartElement (kKey);
var key = (string) keySerializer.Deserialize (reader);
reader.ReadEndElement ();
reader.ReadStartElement (kValue);
var value = (string) valueSerializer.Deserialize (reader);
value = value != UnusedStringValue ? value : key;
reader.ReadEndElement ();
this.Add (key, value);
reader.ReadEndElement ();
reader.MoveToContent ();
}
reader.ReadEndElement ();
}
public void WriteXml (System.Xml.XmlWriter writer)
{
XmlSerializer keySerializer = new XmlSerializer (typeof (string));
XmlSerializer valueSerializer = new XmlSerializer (typeof (string));
foreach (var kvp in this) {
writer.WriteStartElement (kItem);
writer.WriteStartElement (kKey);
keySerializer.Serialize (writer, kvp.Key);
writer.WriteEndElement ();
writer.WriteStartElement (kValue);
if (kvp.Value == UnusedStringValue) {
throw new Exception (String.Format(Errors.E0012, kvp.Key, kvp.Value));
}
valueSerializer.Serialize (writer, kvp.Key == kvp.Value ? UnusedStringValue : kvp.Value);
writer.WriteEndElement ();
writer.WriteEndElement ();
}
}
}
}

Просмотреть файл

@ -4,6 +4,7 @@ using Mono.Options;
using System.Collections.Generic;
using Microsoft.MaciOS.Nnyeah.AssemblyComparator;
using Mono.Cecil;
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.MaciOS.Nnyeah {
class Program {
@ -14,8 +15,7 @@ namespace Microsoft.MaciOS.Nnyeah {
var verbose = false;
var forceOverwrite = false;
var suppressWarnings = false;
var compareAssemblies = false;
string? earlierAssembly = null, laterAssembly = null;
string? xamarinAssembly = null, microsoftAssembly = null;
var options = new OptionSet () {
{ "h|?|help", o => doHelp = true },
@ -24,9 +24,8 @@ namespace Microsoft.MaciOS.Nnyeah {
{ "v|verbose", o => verbose = true },
{ "f|force-overwrite", o => forceOverwrite = true },
{ "s|suppress-warnings", o => suppressWarnings = true },
{ "c|compare-assemblies", o => compareAssemblies = true },
{ "e|earlier-assembly=", f => earlierAssembly = f },
{ "l|later-assembly=", f => laterAssembly = f },
{ "x|xamarin-assembly=", f => xamarinAssembly = f },
{ "m|microsoft-assembly=", f => microsoftAssembly = f },
};
try {
@ -35,63 +34,59 @@ namespace Microsoft.MaciOS.Nnyeah {
doHelp = true;
}
var badForMungingAssembly = !compareAssemblies && (infile is null || outfile is null);
var badForComparingAssemblies = compareAssemblies && (earlierAssembly is null || laterAssembly is null || outfile is null);
var badForMungingAssembly = infile is null || outfile is null;
var badForComparingAssemblies = xamarinAssembly is null || microsoftAssembly is null;
if (doHelp || badForComparingAssemblies || badForMungingAssembly) {
PrintOptions (options, Console.Out);
Environment.Exit (0);
}
if (compareAssemblies) {
try {
CompareAssemblies (earlierAssembly!, laterAssembly!, outfile!, publicOnly: true,
forceOverwrite: forceOverwrite);
} catch (Exception e) {
Console.Error.WriteLine (Errors.E0011, e.Message);
}
} else {
ReworkFile (infile!, outfile!, verbose, forceOverwrite, suppressWarnings);
if (!TryLoadTypeAndModuleMap (xamarinAssembly!, microsoftAssembly!, publicOnly: true,
out var typeAndModuleMap, out var failureReason)) {
Console.Error.WriteLine (Errors.E0011, failureReason);
}
ReworkFile (infile!, outfile!, verbose, forceOverwrite, suppressWarnings);
}
static void CompareAssemblies (string earlier, string later,
string outfile, bool publicOnly, bool forceOverwrite)
static bool TryLoadTypeAndModuleMap (string earlier, string later, bool publicOnly,
[NotNullWhen (returnValue: true)] out TypeAndMemberMap? result,
[NotNullWhen (returnValue: false)] out string? reason)
{
if (File.Exists (outfile) && !forceOverwrite) {
Console.Error.WriteLine (Errors.E0002, outfile);
Environment.Exit (1);
try {
using var ealierFile = TryOpenRead (earlier);
using var laterFile = TryOpenRead (later);
var earlierModule = ModuleDefinition.ReadModule (ealierFile);
var laterModule = ModuleDefinition.ReadModule (laterFile);
var comparingVisitor = new ComparingVisitor (earlierModule, laterModule, publicOnly);
var map = new TypeAndMemberMap ();
comparingVisitor.TypeEvents.NotFound += (s, e) => { map.TypesNotPresent.Add (e.Original); };
comparingVisitor.TypeEvents.Found += (s, e) => { map.TypeMap.Add (e.Original, e.Mapped); };
comparingVisitor.MethodEvents.NotFound += (s, e) => { map.MethodsNotPresent.Add (e.Original); };
comparingVisitor.MethodEvents.Found += (s, e) => { map.MethodMap.Add (e.Original, e.Mapped); };
comparingVisitor.FieldEvents.NotFound += (s, e) => { map.FieldsNotPresent.Add (e.Original); };
comparingVisitor.FieldEvents.Found += (s, e) => { map.FieldMap.Add (e.Original, e.Mapped); };
comparingVisitor.EventEvents.NotFound += (s, e) => { map.EventsNotPresent.Add (e.Original); };
comparingVisitor.EventEvents.Found += (s, e) => { map.EventMap.Add (e.Original, e.Mapped); };
comparingVisitor.PropertyEvents.NotFound += (s, e) => { map.PropertiesNotPresent.Add (e.Original); };
comparingVisitor.PropertyEvents.Found += (s, e) => { map.PropertyMap.Add (e.Original, e.Mapped); };
comparingVisitor.Visit ();
result = map;
reason = null;
return true;
} catch (Exception e) {
result = null;
reason = e.Message;
return false;
}
using var ealierFile = TryOpenRead (earlier);
using var laterFile = TryOpenRead (later);
var earlierModule = ModuleDefinition.ReadModule (ealierFile);
var laterModule = ModuleDefinition.ReadModule (laterFile);
var comparingVisitor = new ComparingVisitor (earlierModule, laterModule, publicOnly);
var map = new TypeAndMemberMap ();
comparingVisitor.TypeEvents.NotFound += (s, e) => { map.TypesNotPresent.Add (e.Original); };
comparingVisitor.TypeEvents.Found += (s, e) => { map.TypeMap.Add (e.Original, e.Mapped); };
comparingVisitor.MethodEvents.NotFound += (s, e) => { map.MethodsNotPresent.Add (e.Original); };
comparingVisitor.MethodEvents.Found += (s, e) => { map.MethodMap.Add (e.Original, e.Mapped); };
comparingVisitor.FieldEvents.NotFound += (s, e) => { map.FieldsNotPresent.Add (e.Original); };
comparingVisitor.FieldEvents.Found += (s, e) => { map.FieldMap.Add (e.Original, e.Mapped); };
comparingVisitor.EventEvents.NotFound += (s, e) => { map.EventsNotPresent.Add (e.Original); };
comparingVisitor.EventEvents.Found += (s, e) => { map.EventMap.Add (e.Original, e.Mapped); };
comparingVisitor.PropertyEvents.NotFound += (s, e) => { map.PropertiesNotPresent.Add (e.Original); };
comparingVisitor.PropertyEvents.Found += (s, e) => { map.PropertyMap.Add (e.Original, e.Mapped); };
comparingVisitor.Visit ();
using var ostm = new FileStream (outfile, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
map.ToXml (ostm);
ostm.Close ();
}