mirror of
https://github.com/MoleskiCoder/EightBitNet.git
synced 2025-08-07 20:25:28 +00:00
Fully embrace reflection to define the debugging parser
This commit is contained in:
@@ -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();
|
||||
|
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
|
@@ -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) { }
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
|
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user