First stab at controlling debug symbols parsing y using attributes.

This commit is contained in:
Adrian Conlon
2024-06-24 17:09:13 +01:00
parent 7e7f10885a
commit ddef969d34
18 changed files with 334 additions and 136 deletions

View File

@@ -5,23 +5,20 @@
namespace Symbols
{
// file id=0,name="sudoku.s",size=9141,mtime=0x6027C7F0,mod=0
public class File : NamedSection
public class File(Parser container) : NamedSection(container)
{
[SectionProperty("size")]
public int Size { get; private set; }
[SectionProperty("mtime", hexadecimal: true)]
public long ModificationTime { get; private set; }
[SectionReference("mod")]
public Symbols.Module Module => this.TakeModuleReference();
public File()
public override void Parse(IDictionary<string, string> entries)
{
_ = this._integer_keys.Add("size");
_ = this._hex_long_keys.Add("mtime");
_ = this._integer_keys.Add("mod");
}
public override void Parse(Parser parent, IDictionary<string, string> entries)
{
base.Parse(parent, entries);
base.Parse(entries);
this.Size = this.TakeInteger("size");
this.ModificationTime = this.TakeLong("mtime");
}

View File

@@ -6,12 +6,16 @@
{
public class IdentifiableSection : Section
{
[SectionProperty("id")]
public int ID { get; private set; }
protected IdentifiableSection() => _ = this._integer_keys.Add("id");
public override void Parse(Parser parent, IDictionary<string, string> entries)
protected IdentifiableSection(Parser container)
: base(container)
{}
public override void Parse(IDictionary<string, string> entries)
{
base.Parse(parent, entries);
base.Parse(entries);
this.ID = this.TakeInteger("id");
}
@@ -55,23 +59,23 @@
#region Specific FK access
protected Module TakeModuleReference(string key = "mod") => this.TakeReference<Module>(key, this._parent?.Modules);
protected Module TakeModuleReference(string key = "mod") => this.TakeReference<Module>(key, this._container?.Modules);
protected File TakeFileReference(string key = "file") => this.TakeReference<File>(key, this._parent?.Files);
protected File TakeFileReference(string key = "file") => this.TakeReference<File>(key, this._container?.Files);
protected Type TakeTypeReference(string key = "type") => this.TakeReference<Type>(key, this._parent?.Types);
protected Type TakeTypeReference(string key = "type") => this.TakeReference<Type>(key, this._container?.Types);
protected Segment TakeSegmentReference(string key = "seg") => this.TakeReference<Segment>(key, this._parent?.Segments);
protected Segment TakeSegmentReference(string key = "seg") => this.TakeReference<Segment>(key, this._container?.Segments);
protected Scope TakeScopeReference(string key = "scope") => this.TakeReference<Scope>(key, this._parent?.Scopes);
protected Scope TakeScopeReference(string key = "scope") => this.TakeReference<Scope>(key, this._container?.Scopes);
protected Scope? MaybeTakeParentReference(string key = "parent") => this.MaybeTakeReference<Scope>(key, this._parent?.Scopes);
protected Scope? MaybeTakeParentReference(string key = "parent") => this.MaybeTakeReference<Scope>(key, this._container?.Scopes);
protected Symbol? MaybeTakeSymbolReference(string key = "sym") => this.MaybeTakeReference<Symbol>(key, this._parent?.Symbols);
protected Symbol? MaybeTakeSymbolReference(string key = "sym") => this.MaybeTakeReference<Symbol>(key, this._container?.Symbols);
protected List<Span> TakeSpanReferences(string key = "span") => this.TakeReferences<Span>(key, this._parent?.Spans);
protected List<Span> TakeSpanReferences(string key = "span") => this.TakeReferences<Span>(key, this._container?.Spans);
protected List<Line> TakeLineReferences(string key) => this.TakeReferences<Line>(key, this._parent?.Lines);
protected List<Line> TakeLineReferences(string key) => this.TakeReferences<Line>(key, this._container?.Lines);
#endregion

View File

@@ -7,18 +7,19 @@
//info csym = 0, file = 3, lib = 0, line = 380, mod = 1, scope = 12, seg = 8, span = 356, sym = 61, type = 3
public class Information : Section
{
public Information()
public Information(Parser container)
: base(container)
{
_ = this._integer_keys.Add("csym");
_ = this._integer_keys.Add("file");
_ = this._integer_keys.Add("lib");
_ = this._integer_keys.Add("line");
_ = this._integer_keys.Add("mod");
_ = this._integer_keys.Add("scope");
_ = this._integer_keys.Add("seg");
_ = this._integer_keys.Add("span");
_ = this._integer_keys.Add("sym");
_ = this._integer_keys.Add("type");
this.AddIntegerKey("csym");
this.AddIntegerKey("file");
this.AddIntegerKey("lib");
this.AddIntegerKey("line");
this.AddIntegerKey("mod");
this.AddIntegerKey("scope");
this.AddIntegerKey("seg");
this.AddIntegerKey("span");
this.AddIntegerKey("sym");
this.AddIntegerKey("type");
}
public int Count(string key) => this.TakeInteger(key);

View File

@@ -5,28 +5,26 @@
namespace Symbols
{
// line id = 268, file = 1, line = 60, type = 2, count = 1, span = 286 + 195
public class Line : IdentifiableSection
public class Line(Parser container) : IdentifiableSection(container)
{
[SectionReference("file")]
public Symbols.File File => this.TakeFileReference();
[SectionProperty("line")]
public int LineNumber { get; private set; }
[SectionReference("type")]
public Symbols.Type Type => this.TakeTypeReference();
[SectionProperty("count")]
public int? Count { get; private set; }
public List<Span> Spans => this.TakeSpanReferences();
public Line()
{
_ = this._integer_keys.Add("file");
_ = this._integer_keys.Add("line");
_ = this._integer_keys.Add("type");
_ = this._integer_keys.Add("count");
_ = this._multiple_keys.Add("span");
}
public override void Parse(Parser parent, IDictionary<string, string> entries)
[SectionReferences("span")]
public List<Span> Spans => this.TakeSpanReferences();
public override void Parse(IDictionary<string, string> entries)
{
base.Parse(parent, entries);
base.Parse(entries);
this.LineNumber = this.TakeInteger("line");
this.Count = this.MaybeTakeInteger("count");
}

View File

@@ -5,11 +5,10 @@
namespace Symbols
{
// mod id=0,name="sudoku.o",file=0
public class Module : NamedSection
public class Module(Parser container) : NamedSection(container)
{
[SectionReference("file")]
public Symbols.File File => this.TakeFileReference();
public Module() => _ = this._integer_keys.Add("file");
}
}
}

View File

@@ -6,13 +6,15 @@
{
public class NamedSection : IdentifiableSection
{
[SectionProperty("name")]
public string? Name { get; private set; }
protected NamedSection() => _ = this._string_keys.Add("name");
protected NamedSection(Parser container)
: base(container) { }
public override void Parse(Parser parent, IDictionary<string, string> entries)
public override void Parse(IDictionary<string, string> entries)
{
base.Parse(parent, entries);
base.Parse(entries);
this.Name = this.TakeString("name");
}
}

View File

@@ -288,7 +288,7 @@
private void ExtractTypes() => this.Extract<Type>("type", this.Types);
private void Extract<T>(string key, List<T> into) where T : IdentifiableSection, new()
private void Extract<T>(string key, List<T> into) where T : IdentifiableSection//, new()
{
if (this._parsed == null)
{
@@ -305,8 +305,9 @@
foreach (var (id, information) in parsed)
{
Debug.Assert(into.Count == id);
var entry = new T();
entry.Parse(this, information);
var entry = (T?)Activator.CreateInstance(typeof(T), this);
Debug.Assert(entry != null);
entry.Parse(information);
into.Add(entry);
}
this.VerifyInformationCount(key, into.Count);
@@ -401,13 +402,13 @@
if (key is "version")
{
this._version = new Version();
this._version.Parse(this, BuildDictionary(parts));
this._version = new Version(this);
this._version.Parse(BuildDictionary(parts));
}
else if (key is "info")
{
this._information = new Information();
this._information.Parse(this, BuildDictionary(parts));
this._information = new Information(this);
this._information.Parse(BuildDictionary(parts));
}
else
{
@@ -467,7 +468,6 @@
return FrozenDictionary.ToFrozenDictionary(dictionary);
}
#endregion
}
}

View File

@@ -7,20 +7,27 @@
//scope id = 0, name = "", mod = 0, size = 1137, span = 355 + 354
//scope id = 1, name = "stack", mod = 0, type = scope, size = 7, parent = 0, span = 15
//scope id = 7, name = "print_box_break_vertical", mod = 0, type = scope, size = 6, parent = 0, sym = 33, span = 72
public class Scope : NamedSection
public class Scope(Parser container) : NamedSection(container)
{
[SectionReference("mod")]
public Symbols.Module Module => this.TakeModuleReference();
[SectionProperty("type")]
public string? Type => this.MaybeTakeString("type");
[SectionProperty("size")]
public int Size { get; private set; }
[SectionReference("parent")]
public Scope? Parent => this.MaybeTakeParentReference();
private bool _symbolAvailable;
private Symbols.Symbol? _symbol;
[SectionReference("sym")]
public Symbols.Symbol? Symbol
{
get
get
{
if (!this._symbolAvailable)
{
@@ -30,21 +37,13 @@
return this._symbol;
}
}
[SectionReferences("span")]
public List<Span> Spans => this.TakeSpanReferences();
public Scope()
public override void Parse(IDictionary<string, string> entries)
{
_ = this._integer_keys.Add("mod");
_ = this._enumeration_keys.Add("type");
_ = this._integer_keys.Add("size");
_ = this._integer_keys.Add("parent");
_ = this._integer_keys.Add("sym");
_ = this._multiple_keys.Add("span");
}
public override void Parse(Parser parent, IDictionary<string, string> entries)
{
base.Parse(parent, entries);
base.Parse(entries);
this.Size = this.TakeInteger("size");
}
}

View File

@@ -5,33 +5,179 @@
namespace Symbols
{
using System.Globalization;
using System.Reflection;
public class Section
{
protected Parser? _parent;
protected readonly Parser _container;
protected readonly Dictionary<string, string> _strings = [];
private readonly HashSet<string> _string_keys = [];
private readonly HashSet<string> _enumeration_keys = [];
protected readonly Dictionary<string, int> _integers = [];
private readonly HashSet<string> _integer_keys = [];
private readonly HashSet<string> _hex_integer_keys = [];
protected readonly Dictionary<string, long> _longs = [];
private readonly HashSet<string> _long_keys = [];
private readonly HashSet<string> _hex_long_keys = [];
protected readonly Dictionary<string, List<int>> _multiples = [];
private readonly HashSet<string> _multiple_keys = [];
protected readonly HashSet<string> _string_keys = [];
protected readonly HashSet<string> _enumeration_keys = [];
protected readonly HashSet<string> _integer_keys = [];
protected readonly HashSet<string> _long_keys = [];
protected readonly HashSet<string> _hex_integer_keys = [];
protected readonly HashSet<string> _hex_long_keys = [];
protected readonly HashSet<string> _multiple_keys = [];
public virtual void Parse(Parser parent, IDictionary<string, string> entries)
protected Section(Parser container)
{
this.ProcessAttributesOfProperties();
this._container = container;
}
public virtual void Parse(IDictionary<string, string> entries)
{
this._parent = parent;
foreach (var entry in entries)
{
this.Parse(entry);
}
}
private void ProcessAttributesOfProperties()
{
var type = this.GetType();
foreach (var property in type.GetProperties())
{
this.ProcessPropertyAttributes(property);
}
}
private void ProcessPropertyAttributes(PropertyInfo property)
{
var attributes = property.GetCustomAttributes(typeof(SectionPropertyAttribute), true);
if (attributes.Length > 0)
{
this.ProcessSectionPropertyAttribute(property.PropertyType, attributes[0]);
}
}
private void ProcessSectionPropertyAttribute(System.Type? type, object attribute)
{
ArgumentNullException.ThrowIfNull(type, nameof(type));
this.ProcessSectionPropertyAttribute(type, (SectionPropertyAttribute)attribute);
}
protected void AddStringKey(string key)
{
if (!this._string_keys.Add(key))
{
throw new InvalidOperationException($"<{key}> already has an entry");
}
}
protected void AddEnumerationKey(string key)
{
if (!this._enumeration_keys.Add(key))
{
throw new InvalidOperationException($"<{key}> already has an entry");
}
}
protected void AddMultiplesKey(string key)
{
if (!this._multiple_keys.Add(key))
{
throw new InvalidOperationException($"<{key}> already has an entry");
}
}
protected void AddHexIntegerKey(string key)
{
if (!this._hex_integer_keys.Add(key))
{
throw new InvalidOperationException($"<{key}> already has an entry");
}
}
protected void AddIntegerKey(string key)
{
if (!this._integer_keys.Add(key))
{
throw new InvalidOperationException($"<{key}> already has an entry");
}
}
protected void AddHexLongKey(string key)
{
if (!this._hex_long_keys.Add(key))
{
throw new InvalidOperationException($"<{key}> already has an entry");
}
}
protected void AddLongKey(string key)
{
if (!this._long_keys.Add(key))
{
throw new InvalidOperationException($"<{key}> already has an entry");
}
}
private void ProcessSectionPropertyAttribute(System.Type originalType, SectionPropertyAttribute attribute)
{
var key = attribute.Key;
var multiples = attribute.Many;
if (multiples)
{
// Type is irrelevant
this.AddMultiplesKey(key);
return;
}
var type = attribute.Type ?? originalType;
var enumeration = attribute.Enumeration;
if (enumeration)
{
System.Diagnostics.Debug.Assert(type == typeof(string), "Enumeration must be of type string");
this.AddEnumerationKey(key);
return;
}
var hex = attribute.Hexadecimal;
if (type == typeof(string))
{
System.Diagnostics.Debug.Assert(!enumeration, "Enumeration case should already have been handled");
System.Diagnostics.Debug.Assert(!hex, "Cannot have a hexadecimal string type");
this.AddStringKey(key);
}
else if (type == typeof(int) || type == typeof(Nullable<int>))
{
if (hex)
{
this.AddHexIntegerKey(key);
}
else
{
this.AddIntegerKey(key);
}
}
else if (type == typeof(long) || type == typeof(Nullable<long>))
{
if (hex)
{
this.AddHexLongKey(key);
}
else
{
this.AddLongKey(key);
}
}
else
{
throw new NotImplementedException($"Property type <{type}> has not been implemented");
}
}
private void Parse(KeyValuePair<string, string> entry)
{
var key = entry.Key;

View File

@@ -0,0 +1,13 @@
namespace EightBit
{
namespace Files
{
namespace Symbols
{
[AttributeUsage(AttributeTargets.Property)]
internal class SectionEnumerationAttribute(string key) : SectionPropertyAttribute(key, enumeration: true)
{
}
}
}
}

View File

@@ -0,0 +1,22 @@
namespace EightBit
{
namespace Files
{
namespace Symbols
{
[AttributeUsage(AttributeTargets.Property)]
internal class SectionPropertyAttribute(string key, System.Type? type = null, bool enumeration = false, bool hexadecimal = false, bool many = false) : Attribute
{
public string Key { get; private set; } = key;
public System.Type? Type { get; private set; } = type;
public bool Enumeration { get; private set; } = enumeration;
public bool Hexadecimal { get; private set; } = hexadecimal;
public bool Many { get; private set; } = many;
}
}
}
}

View File

@@ -0,0 +1,13 @@
namespace EightBit
{
namespace Files
{
namespace Symbols
{
[AttributeUsage(AttributeTargets.Property)]
internal class SectionReferenceAttribute(string key) : SectionPropertyAttribute(key, type: typeof(int))
{
}
}
}
}

View File

@@ -0,0 +1,13 @@
namespace EightBit
{
namespace Files
{
namespace Symbols
{
[AttributeUsage(AttributeTargets.Property)]
internal class SectionReferencesAttribute(string key) : SectionPropertyAttribute(key, many: true)
{
}
}
}
}

View File

@@ -5,28 +5,29 @@
namespace Symbols
{
// seg id=1,name="RODATA",start=0x00F471,size=0x0000,addrsize=absolute,type=ro,oname="sudoku.65b",ooffs=1137
public class Segment : NamedSection
public class Segment(Parser container) : NamedSection(container)
{
[SectionProperty("start", hexadecimal: true)]
public int Start { get; private set; }
[SectionProperty("size", hexadecimal: true)]
public int Size { get; private set; }
[SectionEnumeration("addrsize")]
public string AddressSize => this.TakeString("addrsize");
[SectionEnumeration("type")]
public string Type => this.TakeString("type");
[SectionProperty("oname")]
public string OName => this.TakeString("oname");
[SectionProperty("ooffs")]
public int? OOFFS { get; private set; } // ?? Offsets, perhaps?
public Segment()
public override void Parse(IDictionary<string, string> entries)
{
_ = this._hex_integer_keys.Add("start");
_ = this._hex_integer_keys.Add("size");
_ = this._enumeration_keys.Add("addrsize");
_ = this._enumeration_keys.Add("type");
_ = this._string_keys.Add("oname");
_ = this._integer_keys.Add("ooffs");
}
public override void Parse(Parser parent, IDictionary<string, string> entries)
{
base.Parse(parent, entries);
base.Parse(entries);
this.Start = this.TakeInteger("start");
this.Size = this.TakeInteger("size");
this.OOFFS = this.MaybeTakeInteger("ooffs");

View File

@@ -5,24 +5,23 @@
namespace Symbols
{
//span id = 351, seg = 7, start = 0, size = 2, type = 2
public class Span : IdentifiableSection
public class Span(Parser container) : IdentifiableSection(container)
{
[SectionReference("seg")]
public Symbols.Segment Segment => this.TakeSegmentReference();
[SectionProperty("start")]
public int Start { get; private set; }
[SectionProperty("size")]
public int Size { get; private set; }
[SectionReference("type")]
public Symbols.Type Type => this.TakeTypeReference();
public Span()
public override void Parse(IDictionary<string, string> entries)
{
_ = this._integer_keys.Add("seg");
_ = this._integer_keys.Add("start");
_ = this._integer_keys.Add("size");
_ = this._integer_keys.Add("type");
}
public override void Parse(Parser parent, IDictionary<string, string> entries)
{
base.Parse(parent, entries);
base.Parse(entries);
this.Start = this.TakeInteger("start");
this.Size = this.TakeInteger("size");
}

View File

@@ -7,51 +7,47 @@
using System.Collections.Generic;
// sym id = 16, name = "solve", addrsize = absolute, size = 274, scope = 0, def = 94,ref=144+17+351,val=0xF314,seg=6,type=lab
public class Symbol : NamedSection
public class Symbol(Parser container) : NamedSection(container)
{
[SectionEnumeration("addrsize")]
public string AddressSize => this.TakeString("addrsize");
[SectionProperty("size")]
public int? Size { get; private set; }
[SectionReference("scope")]
public Symbols.Scope Scope => this.TakeScopeReference();
[SectionReferences("def")]
public List<Line> Definitions => this.TakeLineReferences("def"); // Guess
[SectionReferences("ref")]
public List<Line> References => this.TakeLineReferences("ref"); // Guess
[SectionProperty("val", hexadecimal: true)]
public int Value { get; private set; }
[SectionReference("seg")]
public Symbols.Segment Segment => this.TakeSegmentReference();
[SectionEnumeration("type")]
public string Type => this.TakeString("type");
public Symbol()
public override void Parse(IDictionary<string, string> entries)
{
_ = this._enumeration_keys.Add("addrsize");
_ = this._integer_keys.Add("size");
_ = this._integer_keys.Add("scope");
_ = this._multiple_keys.Add("def");
_ = this._multiple_keys.Add("ref");
_ = this._hex_integer_keys.Add("val");
_ = this._integer_keys.Add("seg");
_ = this._enumeration_keys.Add("type");
}
public override void Parse(Parser parent, IDictionary<string, string> entries)
{
base.Parse(parent, entries);
base.Parse(entries);
this.Value = this.TakeInteger("val");
this.Size = this.MaybeTakeInteger("size");
if (this.Type is "lab")
{
this._parent?.AddLabel(this);
this._container?.AddLabel(this);
}
else if (this.Type is "equ")
{
this._parent?.AddEquate(this);
this._container?.AddEquate(this);
}
else
{

View File

@@ -5,11 +5,10 @@
namespace Symbols
{
// type id = 0, val = "800920"
public class Type : IdentifiableSection
public class Type(Parser container) : IdentifiableSection(container)
{
[SectionProperty("val")]
public string Value => this.TakeString("val");
public Type() => _ = this._string_keys.Add("val");
}
}
}

View File

@@ -5,17 +5,13 @@
namespace Symbols
{
//version major = 2, minor = 0
public class Version : Section
public class Version(Parser container) : Section(container)
{
[SectionProperty("major")]
public int Major => this.TakeInteger("major");
[SectionProperty("minor")]
public int Minor => this.TakeInteger("minor");
public Version()
{
_ = this._integer_keys.Add("major");
_ = this._integer_keys.Add("minor");
}
}
}
}