Skip to content

Commit

Permalink
Lets not use serialization. Just pull out the elements.
Browse files Browse the repository at this point in the history
Combine LastWriteTimeUtc into AssemblyInfo.
  • Loading branch information
wasabii committed Oct 18, 2023
1 parent 1412389 commit 7ae782c
Showing 1 changed file with 35 additions and 39 deletions.
74 changes: 35 additions & 39 deletions src/IKVM.MSBuild.Tasks/IkvmAssemblyInfoUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Xml.Serialization;

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
Expand All @@ -22,12 +21,13 @@ public class IkvmAssemblyInfoUtil
const string XML_ASSEMBLY_ELEMENT_NAME = "Assembly";
const string XML_PATH_ATTRIBUTE_NAME = "Path";
const string XML_LAST_WRITE_TIME_UTC_ATTRIBUTE_NAME = "LastWriteTimeUtc";
const string XML_ASSEMBLY_INFO_ELEMENT_NAME = "AssemblyInfo";
const string XML_NAME_ATTRIBUTE_NAME = "Name";
const string XML_MVID_ATTRIBUTE_NAME = "Mvid";
const string XML_REFERENCE_ELEMENT_NAME = "Reference";

/// <summary>
/// Defines the cached information per assembly.
/// </summary>
[XmlRoot(XML_ASSEMBLY_INFO_ELEMENT_NAME)]
public struct AssemblyInfo
{

Expand All @@ -38,9 +38,10 @@ public struct AssemblyInfo
/// <param name="name"></param>
/// <param name="mvid"></param>
/// <param name="references"></param>
public AssemblyInfo(string path, string name, Guid mvid, List<string> references)
public AssemblyInfo(string path, DateTime lastWriteTimeUtc, string name, Guid mvid, string[] references)
{
Path = path;
LastWriteTimeUtc = lastWriteTimeUtc;
Name = name;
Mvid = mvid;
References = references ?? throw new ArgumentNullException(nameof(references));
Expand All @@ -49,33 +50,32 @@ public AssemblyInfo(string path, string name, Guid mvid, List<string> references
/// <summary>
/// Path to the assembly from which this information was derived.
/// </summary>
[XmlAttribute("Path")]
public string Path { get; set; }

/// <summary>
/// Gets the last write time of the assembly.
/// </summary>
public DateTime LastWriteTimeUtc { get; set; }

/// <summary>
/// Name of the assembly.
/// </summary>
[XmlAttribute("Name")]
public string Name { get; set; }

/// <summary>
/// Gets the MVID of the assembly.
/// </summary>
[XmlAttribute("Mvid")]
public Guid Mvid { get; set; }

/// <summary>
/// Names of the references of the assembly.
/// </summary>
[XmlElement("Reference")]
public List<string> References { get; set; }
public string[] References { get; set; }

}

readonly static XmlSerializer assemblyInfoSerializer = new XmlSerializer(typeof(AssemblyInfo));

readonly Dictionary<string, (DateTime LastWriteTimeUtc, AssemblyInfo? Info)> state = new();
readonly ConcurrentDictionary<string, Task<(DateTime LastWriteTimeUtc, AssemblyInfo? Info)>> cache = new();
readonly Dictionary<string, AssemblyInfo> state = new();
readonly ConcurrentDictionary<string, Task<AssemblyInfo?>> cache = new();

/// <summary>
/// Initializes a new instance.
Expand All @@ -98,22 +98,15 @@ public void LoadStateXml(XElement root)
foreach (var element in root.Elements(XML_ASSEMBLY_ELEMENT_NAME))
{
var path = (string)element.Attribute(XML_PATH_ATTRIBUTE_NAME);
if (path == null)
continue;

var lastWriteTimeUtc = (DateTime?)element.Attribute(XML_LAST_WRITE_TIME_UTC_ATTRIBUTE_NAME);
if (lastWriteTimeUtc == null)
continue;

var assemblyInfoXml = new XDocument(element.Element(XML_ASSEMBLY_INFO_ELEMENT_NAME));
if (assemblyInfoXml == null)
continue;
var name = (string)element.Attribute(XML_NAME_ATTRIBUTE_NAME);
var mvid = (Guid?)element.Attribute(XML_MVID_ATTRIBUTE_NAME);
var references = element.Elements(XML_REFERENCE_ELEMENT_NAME).Cast<string>().ToArray();

var assemblyInfo = (AssemblyInfo?)assemblyInfoSerializer.Deserialize(assemblyInfoXml.CreateReader());
if (assemblyInfo == null)
if (path == null || lastWriteTimeUtc == null || name == null || mvid == null)
continue;

state[path] = (lastWriteTimeUtc.Value, assemblyInfo);
state[path] = new AssemblyInfo(path, lastWriteTimeUtc.Value, name, mvid.Value, references);
}
}

Expand All @@ -126,14 +119,17 @@ public async System.Threading.Tasks.Task SaveStateXmlAsync(XElement root)
{
foreach (var i in cache)
{
var (lastWriteTimeUtc, info) = await i.Value;

// serialize assembly info structure
var infoXmlDoc = new XDocument();
using (var infoXmlWrt = infoXmlDoc.CreateWriter())
assemblyInfoSerializer.Serialize(infoXmlWrt, info);
var info = await i.Value;
if (info == null)
continue;

root.Add(new XElement(XML_ASSEMBLY_ELEMENT_NAME, new XAttribute(XML_PATH_ATTRIBUTE_NAME, i.Key), new XAttribute(XML_LAST_WRITE_TIME_UTC_ATTRIBUTE_NAME, lastWriteTimeUtc), infoXmlDoc.Root));
root.Add(
new XElement(XML_ASSEMBLY_ELEMENT_NAME,
new XAttribute(XML_PATH_ATTRIBUTE_NAME, i.Key),
new XAttribute(XML_LAST_WRITE_TIME_UTC_ATTRIBUTE_NAME, info.Value.LastWriteTimeUtc),
new XAttribute(XML_NAME_ATTRIBUTE_NAME, info.Value.Name),
new XAttribute(XML_MVID_ATTRIBUTE_NAME, info.Value.Mvid),
info.Value.References.Select(i => new XElement(XML_REFERENCE_ELEMENT_NAME, i))));
}
}

Expand All @@ -149,7 +145,7 @@ public async System.Threading.Tasks.Task SaveStateXmlAsync(XElement root)
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentException($"'{nameof(path)}' cannot be null or whitespace.", nameof(path));

return (await cache.GetOrAdd(path, path => CreateAssemblyInfoAsync(path, log, cancellationToken))).Info;
return await cache.GetOrAdd(path, path => CreateAssemblyInfoAsync(path, log, cancellationToken));
}

/// <summary>
Expand All @@ -158,9 +154,9 @@ public async System.Threading.Tasks.Task SaveStateXmlAsync(XElement root)
/// <param name="path"></param>
/// <param name="log"></param>
/// <returns></returns>
Task<(DateTime LastWriteTimeUtc, AssemblyInfo? Info)> CreateAssemblyInfoAsync(string path, TaskLoggingHelper log, CancellationToken cancellationToken)
Task<AssemblyInfo?> CreateAssemblyInfoAsync(string path, TaskLoggingHelper log, CancellationToken cancellationToken)
{
return System.Threading.Tasks.Task.Run(() =>
return System.Threading.Tasks.Task.Run<AssemblyInfo?>(() =>
{
try
{
Expand All @@ -169,26 +165,26 @@ public async System.Threading.Tasks.Task SaveStateXmlAsync(XElement root)
// check if loaded state contains up to date information
if (state.TryGetValue(path, out var entry))
if (entry.LastWriteTimeUtc == lastWriteTimeUtc)
return (lastWriteTimeUtc, entry.Info);
return entry;

try
{
log?.LogMessage(MessageImportance.Low, "Loading assembly info from '{0}'.", path);
using var fsstm = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
using var perdr = new PEReader(fsstm);
var mrdr = perdr.GetMetadataReader();
return (lastWriteTimeUtc, new AssemblyInfo(path, mrdr.GetString(mrdr.GetAssemblyDefinition().Name), mrdr.GetGuid(mrdr.GetModuleDefinition().Mvid), mrdr.AssemblyReferences.Select(i => mrdr.GetString(mrdr.GetAssemblyReference(i).Name)).ToList()));
return new AssemblyInfo(path, lastWriteTimeUtc, mrdr.GetString(mrdr.GetAssemblyDefinition().Name), mrdr.GetGuid(mrdr.GetModuleDefinition().Mvid), mrdr.AssemblyReferences.Select(i => mrdr.GetString(mrdr.GetAssemblyReference(i).Name)).ToArray());
}
catch (Exception e)
{
log?.LogWarning("Exception loading assembly info from '{0}': {1}", path, e.Message);
return (lastWriteTimeUtc, null);
return null;
}
}
catch (Exception e)
{
log?.LogWarning("Exception loading assembly info from '{0}': {1}", path, e.Message);
return (default, null);
return null;
}
});
}
Expand Down

0 comments on commit 7ae782c

Please sign in to comment.