[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:
Родитель
b72e7be297
Коммит
1c7d63f393
|
@ -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 ();
|
||||
}
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче