Fully embrace reflection to define the debugging parser

This commit is contained in:
Adrian Conlon
2024-09-19 11:49:44 +01:00
parent b4e130aa9c
commit 5d796ef669
14 changed files with 197 additions and 126 deletions

View File

@@ -8,10 +8,10 @@
public sealed class File(Parser container) : NamedSection(container)
{
[SectionProperty("size")]
public int Size => this.TakeInteger("size");
public int Size { get; private set; }
[SectionProperty("mtime")]
public DateTime ModificationTime => this.TakeDateTime("mtime");
public DateTime ModificationTime { get; private set; }
[SectionReference("mod")]
public Symbols.Module Module => this.TakeModuleReference();

View File

@@ -9,7 +9,7 @@
public class IdentifiableSection : Section
{
[SectionProperty("id")]
public int ID { get; private set; }
public int ID { get; protected set; }
protected IdentifiableSection(Parser container)
: base(container)
@@ -76,12 +76,6 @@
#endregion
#endregion
public override void Parse(IDictionary<string, string> entries)
{
base.Parse(entries);
this.ID = this.TakeInteger("id");
}
}
}
}

View File

@@ -5,26 +5,39 @@
namespace Symbols
{
//info csym = 0, file = 3, lib = 0, line = 380, mod = 1, scope = 12, seg = 8, span = 356, sym = 61, type = 3
public sealed class Information : Section
public sealed class Information(Parser container) : Section(container)
{
public Information(Parser container)
: base(container)
{
this.ProcessAttributesOfProperties();
var properties = _sectionPropertiesCache[this.GetType()];
properties.AddIntegerKey("csym");
properties.AddIntegerKey("file");
properties.AddIntegerKey("lib");
properties.AddIntegerKey("line");
properties.AddIntegerKey("mod");
properties.AddIntegerKey("scope");
properties.AddIntegerKey("seg");
properties.AddIntegerKey("span");
properties.AddIntegerKey("sym");
properties.AddIntegerKey("type");
}
[SectionProperty("csym")]
public int CSymbol { get; private set; }
public int Count(string key) => this.TakeInteger(key);
[SectionProperty("file")]
public int File { get; private set; }
[SectionProperty("lib")]
public int Library { get; private set; }
[SectionProperty("line")]
public int Line { get; private set; }
[SectionProperty("mod")]
public int Module { get; private set; }
[SectionProperty("scope")]
public int Scope { get; private set; }
[SectionProperty("seg")]
public int Segment { get; private set; }
[SectionProperty("span")]
public int Span { get; private set; }
[SectionProperty("sym")]
public int Symbol { get; private set; }
[SectionProperty("type")]
public int Type { get; private set; }
public int Count(string key) => this.GetValueT<int>(key);
}
}
}

View File

@@ -11,13 +11,13 @@
public Symbols.File File => this.TakeFileReference();
[SectionProperty("line")]
public int LineNumber => this.TakeInteger("line");
public int LineNumber { get; private set; }
[SectionReference("type")]
public Symbols.Type Type => this.TakeTypeReference();
[SectionProperty("count")]
public int? Count => this.MaybeTakeInteger("count");
public int? Count { get; private set; }
[SectionReferences("span")]
public List<Span> Spans => this.TakeSpanReferences();

View File

@@ -7,7 +7,7 @@
public class NamedSection : IdentifiableSection
{
[SectionProperty("name")]
public string? Name => this.TakeString("name");
public string? Name { get; protected set; }
protected NamedSection(Parser container)
: base(container) { }

View File

@@ -336,20 +336,13 @@
#region Metadata lookup
private int InformationCount(string key)
private void VerifyInformationCount(string key, int actual)
{
if (this._information == null)
Debug.Assert(this._information != null);
var expected = this._information?.Count(key);
if (expected != actual)
{
throw new InvalidOperationException("Information section has not been initialised");
}
return this._information.Count(key);
}
private void VerifyInformationCount(string key, int extracted)
{
if (extracted != this.InformationCount(key))
{
throw new InvalidOperationException($"Invalid symbol file format (Information/{key} section count mismatch)");
throw new InvalidOperationException($"information count mismatch for {key}. Expected {expected}, actual {actual}");
}
}
@@ -472,24 +465,6 @@
}
var dictionary = BuildDictionary(parts);
if (!dictionary.TryGetValue("id", out var id))
{
throw new InvalidOperationException("Invalid symbol file format (definition does not have id)");
}
var identifier = int.Parse(id);
if (this._information == null)
{
throw new InvalidOperationException("Invalid symbol file format (info section has not been parsed)");
}
var count = this._information.Count(key);
if ((identifier + 1) > count)
{
throw new InvalidOperationException($"Invalid symbol file format (No count information available for {section})");
}
section.Add(dictionary);
}

View File

@@ -9,6 +9,9 @@
public sealed class ReflectedSectionProperties
{
private readonly Dictionary<string, string> _entriesToProperties = [];
private readonly Dictionary<string, string> _propertiesToEntries = [];
public HashSet<string> StringKeys { get; } = [];
public HashSet<string> EnumerationKeys { get; } = [];
public HashSet<string> IntegerKeys { get; } = [];
@@ -18,6 +21,8 @@
public HashSet<string> DateTimeKeys { get; } = [];
public HashSet<string> MultipleKeys { get; } = [];
public Dictionary<string, PropertyInfo> Properties { get; } = [];
public ReflectedSectionProperties(System.Type type)
{
foreach (var property in type.GetProperties())
@@ -26,20 +31,66 @@
}
}
public string GetPropertyNameFromEntryName(string name)
{
var found = this._entriesToProperties.TryGetValue(name, out var propertyName);
if (!found)
{
throw new ArgumentOutOfRangeException(nameof(name), name, "Missing property mapping");
}
Debug.Assert(propertyName != null);
return propertyName;
}
public string GetEntryNameFromPropertyName(string name)
{
var found = this._propertiesToEntries.TryGetValue(name, out var entryName);
if (!found)
{
throw new ArgumentOutOfRangeException(nameof(name), name, "Missing entry mapping");
}
Debug.Assert(entryName != null);
return entryName;
}
public object? GetValue(object? obj, string key)
{
var propertyName = this.GetPropertyNameFromEntryName(key);
var propertyAvailable = this.Properties.TryGetValue(propertyName, out var property);
Debug.Assert(propertyAvailable);
Debug.Assert(property != null);
var readable = property?.CanRead;
Debug.Assert(readable != null);
Debug.Assert(readable.Value);
return property?.GetValue(obj);
}
public T? MaybeGetValue<T>(object? obj, string key) => (T?)this.GetValue(obj, key);
public T GetValueT<T>(object? obj, string key)
{
var possible = this.MaybeGetValue<T>(obj, key);
return possible != null ? possible : throw new ArgumentOutOfRangeException(nameof(key), key, "Property read issue");
}
private void ProcessPropertyAttributes(PropertyInfo property)
{
var attributes = property.GetCustomAttributes(typeof(SectionPropertyAttribute), true);
if (attributes.Length > 0)
{
Debug.Assert(attributes.Length == 1, "Too many section property attributes");
this.ProcessSectionPropertyAttribute(property.PropertyType, attributes[0]);
var succeeded = this.Properties.TryAdd(property.Name, property);
Debug.Assert(succeeded, $"Unable to add property lookup {property.Name}");
this.ProcessSectionPropertyAttribute(property.PropertyType, property.Name, attributes[0]);
}
}
private void ProcessSectionPropertyAttribute(System.Type? type, object attribute)
private void ProcessSectionPropertyAttribute(System.Type? type, string name, object attribute)
{
ArgumentNullException.ThrowIfNull(type, nameof(type));
this.ProcessSectionPropertyAttribute(type, (SectionPropertyAttribute)attribute);
this.ProcessSectionPropertyAttribute(type, name, (SectionPropertyAttribute)attribute);
}
public void AddStringKey(string key)
@@ -90,10 +141,20 @@
Debug.Assert(added, $"<{key}> already has an entry");
}
private void ProcessSectionPropertyAttribute(System.Type originalType, SectionPropertyAttribute attribute)
private void ProcessSectionPropertyAttribute(System.Type originalType, string name, SectionPropertyAttribute attribute)
{
var key = attribute.Key;
if (!_entriesToProperties.TryAdd(key, name))
{
throw new InvalidOperationException($"Entry {key} from attribute has already been added");
}
if (!_propertiesToEntries.TryAdd(name, key))
{
throw new ArgumentOutOfRangeException(nameof(name), name, "Property has already been added");
}
var multiples = attribute.Many;
if (multiples)
{

View File

@@ -13,7 +13,7 @@
public Symbols.Module Module => this.TakeModuleReference();
[SectionProperty("type")]
public string? Type => this.MaybeTakeString("type");
public string? Type { get; private set; }
[SectionProperty("size")]
public int Size { get; private set; }
@@ -40,12 +40,6 @@
[SectionReferences("span")]
public List<Span> Spans => this.TakeSpanReferences();
public override void Parse(IDictionary<string, string> entries)
{
base.Parse(entries);
this.Size = this.TakeInteger("size");
}
}
}
}

View File

@@ -8,6 +8,7 @@
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
using System.Reflection;
public class Section
{
@@ -16,10 +17,7 @@
protected readonly Parser _container;
protected readonly Dictionary<string, string> _strings = [];
protected readonly Dictionary<string, int> _integers = [];
protected readonly Dictionary<string, long> _longs = [];
protected readonly Dictionary<string, DateTime> _dateTimes = [];
protected readonly Dictionary<string, List<int>> _multiples = [];
protected ReflectedSectionProperties SectionProperties
@@ -33,6 +31,7 @@
return properties;
}
}
protected Section(Parser container) => this._container = container;
protected void ProcessAttributesOfProperties()
@@ -45,6 +44,12 @@
}
}
public object? GetValue(string key) => this.SectionProperties.GetValue(this, key);
public T? MaybeGetValue<T>(string key) => this.SectionProperties.MaybeGetValue<T>(this, key);
public T GetValueT<T>(string key) => this.SectionProperties.GetValueT<T>(this, key);
public virtual void Parse(IDictionary<string, string> entries)
{
this.ProcessAttributesOfProperties();
@@ -54,59 +59,94 @@
}
}
private void Parse(KeyValuePair<string, string> entry)
private void Parse(KeyValuePair<string, string> entry) => this.Parse(entry.Key, entry.Value);
private void Parse(string key, string value)
{
var key = entry.Key;
var value = entry.Value;
if (this.SectionProperties.StringKeys.Contains(key))
var sectionProperties = this.SectionProperties;
var propertyName = sectionProperties.GetPropertyNameFromEntryName(key);
var propertyAvailable = sectionProperties.Properties.TryGetValue(propertyName, out var property);
if (!propertyAvailable)
{
this._strings.Add(key, ExtractString(value));
throw new InvalidOperationException($"Unable to locate property name {propertyName} using reflection");
}
else if (this.SectionProperties.EnumerationKeys.Contains(key))
Debug.Assert(property != null);
this.Parse(property, key, value);
}
private void Parse(PropertyInfo property, string key, string value)
{
if (property.CanWrite)
{
this._strings.Add(key, ExtractEnumeration(value));
this.ParseValueProperty(property, key, value); }
else
{
this.ParseReferenceProperty(key, value);
}
else if (this.SectionProperties.IntegerKeys.Contains(key))
}
private void ParseReferenceProperty(string key, string value)
{
var sectionProperties = this.SectionProperties;
if (sectionProperties.IntegerKeys.Contains(key))
{
this._integers.Add(key, ExtractInteger(value));
}
else if (this.SectionProperties.HexIntegerKeys.Contains(key))
{
this._integers.Add(key, ExtractHexInteger(value));
}
else if (this.SectionProperties.LongKeys.Contains(key))
{
this._longs.Add(key, ExtractLong(value));
}
else if (this.SectionProperties.HexLongKeys.Contains(key))
{
this._longs.Add(key, ExtractHexLong(value));
}
else if (this.SectionProperties.DateTimeKeys.Contains(key))
{
this._dateTimes.Add(key, ExtractDateTime(value));
}
else if (this.SectionProperties.MultipleKeys.Contains(key))
else if (sectionProperties.MultipleKeys.Contains(key))
{
this._multiples.Add(key, ExtractCompoundInteger(value));
}
else
{
throw new InvalidOperationException($"Doesn't appear to be a valid reference type: {key}");
}
}
private void ParseValueProperty(PropertyInfo property, string key, string value)
{
var sectionProperties = this.SectionProperties;
if (sectionProperties.StringKeys.Contains(key))
{
property?.SetValue(this, ExtractString(value));
}
else if (sectionProperties.EnumerationKeys.Contains(key))
{
property?.SetValue(this, ExtractEnumeration(value));
}
else if (sectionProperties.IntegerKeys.Contains(key))
{
property?.SetValue(this, ExtractInteger(value));
}
else if (sectionProperties.HexIntegerKeys.Contains(key))
{
property?.SetValue(this, ExtractHexInteger(value));
}
else if (sectionProperties.LongKeys.Contains(key))
{
property?.SetValue(this, ExtractLong(value));
}
else if (sectionProperties.HexLongKeys.Contains(key))
{
property?.SetValue(this, ExtractHexLong(value));
}
else if (sectionProperties.DateTimeKeys.Contains(key))
{
property?.SetValue(this, ExtractDateTime(value));
}
else if (sectionProperties.MultipleKeys.Contains(key))
{
property?.SetValue(this, ExtractCompoundInteger(value));
}
else
{
throw new InvalidOperationException($"Section: {key} has not been categorised");
}
}
protected int? MaybeTakeInteger(string key) => this._integers.TryGetValue(key, out var value) ? value : null;
protected long? MaybeTakeLong(string key) => this._longs.TryGetValue(key, out var value) ? value : null;
protected DateTime? MaybeTakeDateTime(string key) => this._dateTimes.TryGetValue(key, out var value) ? value : null;
protected string? MaybeTakeString(string key) => this._strings.TryGetValue(key, out var value) ? value : null;
protected List<int>? MaybeTakeMultiple(string key) => this._multiples.TryGetValue(key, out var value) ? value : null;
protected int TakeInteger(string key) => this.MaybeTakeInteger(key) ?? throw new ArgumentOutOfRangeException(nameof(key), key, "Missing integer entry in section");
protected long TakeLong(string key) => this.MaybeTakeLong(key) ?? throw new ArgumentOutOfRangeException(nameof(key), key, "Missing long integer entry in section");
protected DateTime TakeDateTime(string key) => this.MaybeTakeDateTime(key) ?? throw new ArgumentOutOfRangeException(nameof(key), key, "Missing DateTime entry in section");
protected string TakeString(string key) => this.MaybeTakeString(key) ?? throw new ArgumentOutOfRangeException(nameof(key), key, "Missing string entry in section");
protected List<int> TakeMultiple(string key) => this.MaybeTakeMultiple(key) ?? throw new ArgumentOutOfRangeException(nameof(key), key, "Missing multiple entry in section");
protected static string ExtractString(string value) => value.Trim('"');
protected static string ExtractEnumeration(string value) => value;

View File

@@ -8,22 +8,22 @@
public sealed class Segment(Parser container) : NamedSection(container)
{
[SectionProperty("start", hexadecimal: true)]
public int Start => this.TakeInteger("start");
public int Start { get; private set; }
[SectionProperty("size", hexadecimal: true)]
public int Size => this.TakeInteger("size");
public int Size { get; private set; }
[SectionEnumeration("addrsize")]
public string AddressSize => this.TakeString("addrsize");
public string? AddressSize { get; private set; }
[SectionEnumeration("type")]
public string Type => this.TakeString("type");
public string? Type { get; private set; }
[SectionProperty("oname")]
public string OName => this.TakeString("oname");
public string? OName { get; private set; }
[SectionProperty("ooffs")]
public int? OOFFS => this.MaybeTakeInteger("ooffs"); // ?? Offsets, perhaps?
public int? OOFFS { get; private set; }
}
}
}

View File

@@ -11,10 +11,10 @@
public Symbols.Segment Segment => this.TakeSegmentReference();
[SectionProperty("start")]
public int Start => this.TakeInteger("start");
public int Start { get; private set; }
[SectionProperty("size")]
public int Size => this.TakeInteger("size");
public int Size { get; private set; }
[SectionReference("type")]
public Symbols.Type Type => this.TakeTypeReference();

View File

@@ -10,10 +10,10 @@
public sealed class Symbol(Parser container) : NamedSection(container)
{
[SectionEnumeration("addrsize")]
public string AddressSize => this.TakeString("addrsize");
public string? AddressSize { get; private set; }
[SectionProperty("size")]
public int? Size => this.MaybeTakeInteger("size");
public int? Size { get; private set; }
[SectionReference("scope")]
public Symbols.Scope Scope => this.TakeScopeReference();
@@ -31,13 +31,7 @@
public Symbols.Segment Segment => this.TakeSegmentReference();
[SectionEnumeration("type")]
public string Type => this.TakeString("type");
public override void Parse(IDictionary<string, string> entries)
{
base.Parse(entries);
this.Value = this.TakeInteger("val");
}
public string? Type { get; private set; }
}
}
}

View File

@@ -8,7 +8,7 @@
public sealed class Type(Parser container) : IdentifiableSection(container)
{
[SectionProperty("val")]
public string Value => this.TakeString("val");
public string? Value { get; private set; }
}
}
}

View File

@@ -8,10 +8,10 @@
public sealed class Version(Parser container) : Section(container)
{
[SectionProperty("major")]
public int Major => this.TakeInteger("major");
public int Major { get; private set; }
[SectionProperty("minor")]
public int Minor => this.TakeInteger("minor");
public int Minor { get; private set; }
}
}
}