Add type exports to disassembler

This commit is contained in:
Yoshi Askharoun
2024-02-08 13:32:44 -06:00
parent 42a6ea5795
commit 9a2893a146
4 changed files with 145 additions and 31 deletions
+106 -22
View File
@@ -3,56 +3,118 @@ using Microsoft.Iris.Asm.Models;
using Microsoft.Iris.Markup; using Microsoft.Iris.Markup;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Microsoft.Iris.Asm; namespace Microsoft.Iris.Asm;
public class Disassembler public class Disassembler
{ {
private readonly MarkupLoadResult _loadResult; private readonly MarkupLoadResult _loadResult;
private readonly Dictionary<string, string> _importedUris;
private readonly Dictionary<uint, List<Label>> _offsetLabelMap = new();
private Disassembler(MarkupLoadResult loadResult) private Disassembler(MarkupLoadResult loadResult)
{ {
_loadResult = loadResult; _loadResult = loadResult;
_importedUris = new()
{
[_loadResult.Uri] = "me",
["http://schemas.microsoft.com/2007/uix"] = null
};
} }
public static Disassembler Load(MarkupLoadResult loadResult) => new(loadResult); public static Disassembler Load(MarkupLoadResult loadResult) => new(loadResult);
public IEnumerable<Directive> GetExports()
{
foreach (var typeSchema in _loadResult.ExportTable)
{
var labelPrefix = typeSchema.Name;
var baseName = GetQualifiedName(typeSchema.Base);
if (typeSchema is not MarkupTypeSchema markupTypeSchema)
throw new Exception($"Disassembler failed to disassemble export {typeSchema}, '{typeSchema.GetType()}' is not supported.");
yield return new ExportDirective(labelPrefix, markupTypeSchema.ListenerCount, markupTypeSchema.Base.Name);
var propOffset = markupTypeSchema.InitializePropertiesOffset;
InsertLabel(propOffset, ExportDirective.GetInitializePropertiesLabel(labelPrefix));
var loclOffset = markupTypeSchema.InitializeLocalsInputOffset;
InsertLabel(loclOffset, ExportDirective.GetInitializeLocalsInputLabel(labelPrefix));
var contOffset = markupTypeSchema.InitializeContentOffset;
InsertLabel(contOffset, ExportDirective.GetInitializeContentLabel(labelPrefix));
if (markupTypeSchema.InitialEvaluateOffsets != null)
{
for (int i = 0; i < markupTypeSchema.InitialEvaluateOffsets.Length; i++)
{
uint offset = markupTypeSchema.InitialEvaluateOffsets[i];
var labelName = $"{ExportDirective.GetInitializeContentLabel(labelPrefix)}{i:N}";
InsertLabel(offset, labelName);
}
}
if (markupTypeSchema.FinalEvaluateOffsets != null)
{
for (int i = 0; i < markupTypeSchema.FinalEvaluateOffsets.Length; i++)
{
uint offset = markupTypeSchema.FinalEvaluateOffsets[i];
var labelName = $"{ExportDirective.GetFinalEvaluateOffsetsLabelPrefix(labelPrefix)}{i:N}";
InsertLabel(offset, labelName);
}
}
if (markupTypeSchema.RefreshGroupOffsets != null)
{
for (int i = 0; i < markupTypeSchema.RefreshGroupOffsets.Length; i++)
{
uint offset = markupTypeSchema.RefreshGroupOffsets[i];
var labelName = $"{ExportDirective.GetRefreshGroupOffsetsLabelPrefix(labelPrefix)}{i:N}";
InsertLabel(offset, labelName);
}
}
}
}
public IEnumerable<IImport> GetImports() public IEnumerable<IImport> GetImports()
{ {
// Keep track of what has already been imported. Skip self and default UIX namespace. // Ues _importedUris to keep track of what has already been imported.
Dictionary<string, string> importedUris = new() { // Skip self and default UIX namespace.
[_loadResult.Uri] = "me",
["http://schemas.microsoft.com/2007/uix"] = null
};
foreach (var typeImport in _loadResult.ImportTables.TypeImports) foreach (var typeImport in _loadResult.ImportTables.TypeImports)
{ {
var uri = typeImport.Owner.Uri; var uri = typeImport.Owner.Uri;
if (!importedUris.TryGetValue(uri, out var namespacePrefix)) if (!_importedUris.TryGetValue(uri, out var namespacePrefix))
{ {
namespacePrefix = uri; namespacePrefix = uri;
var schemeLength = uri.IndexOf("://"); var schemeLength = uri.IndexOf("://");
if (schemeLength > 0) if (schemeLength > 0)
{
var scheme = uri[..schemeLength];
if (scheme == "assembly")
{ {
// Assume the URI represents a C# namespace var scheme = uri[..schemeLength];
if (scheme == "assembly")
{
// Assume the URI represents a C# namespace
namespacePrefix = uri.Split('.', '/', '\\', '!')[^1]; namespacePrefix = uri.Split('.', '/', '\\', '!')[^1];
// Remove the extra assembly info // Remove the extra assembly info
uri = uri.Split(',')[0]; uri = uri.Split(',')[0];
} }
else else
{ {
// Assume the URI represents a file, // Assume the URI represents a file,
// skip the extension // skip the extension
namespacePrefix = uri.Split('.', '/', '\\', '!')[^2]; namespacePrefix = uri.Split('.', '/', '\\', '!')[^2];
} }
} }
namespacePrefix = namespacePrefix.Camelize(); namespacePrefix = namespacePrefix.Camelize();
importedUris.Add(uri, namespacePrefix); _importedUris.Add(uri, namespacePrefix);
yield return new NamespaceImport(uri, namespacePrefix); yield return new NamespaceImport(uri, namespacePrefix);
} }
@@ -69,6 +131,10 @@ public class Disassembler
while (reader.CurrentOffset < reader.Size) while (reader.CurrentOffset < reader.Size)
{ {
if (_offsetLabelMap.TryGetValue(reader.CurrentOffset, out var labels))
foreach (var label in labels)
yield return label;
var opCode = (OpCode)reader.ReadByte(); var opCode = (OpCode)reader.ReadByte();
switch (opCode) switch (opCode)
@@ -183,10 +249,28 @@ public class Disassembler
_loadResult.Load(LoadPass.Full); _loadResult.Load(LoadPass.Full);
_loadResult.Load(LoadPass.Done); _loadResult.Load(LoadPass.Done);
List<IImport> imports = new(GetImports()); List<IDirective> directives = GetImports().Cast<IDirective>()
.Concat(GetExports())
.ToList();
List<IBodyItem> body = new(GetBody()); List<IBodyItem> body = new(GetBody());
Program asmProgram = new(imports, body); Program asmProgram = new(directives, body);
return asmProgram.ToString(); return asmProgram.ToString();
} }
private string GetQualifiedName(TypeSchema schema)
{
_importedUris.TryGetValue(schema.Owner.Uri, out string prefix);
return prefix != null
? $"{prefix}:{schema.Name}"
: schema.Name;
}
private void InsertLabel(uint offset, string labelName)
{
if (!_offsetLabelMap.TryGetValue(offset, out var labels))
labels = _offsetLabelMap[offset] = new(1);
labels.Add(new(labelName));
}
} }
+16 -1
View File
@@ -53,7 +53,22 @@ partial class Lexer
if (!labelPrefixResult.WasSuccessful) if (!labelPrefixResult.WasSuccessful)
return Result.Failure<IImport>(input, "Invalid export directive", ["Expected prefix of labels to export"]); return Result.Failure<IImport>(input, "Invalid export directive", ["Expected prefix of labels to export"]);
directive = new ExportDirective(labelPrefixResult.Value) var listenerCountResult = WholeNumber.Token()(input);
input = listenerCountResult.Remainder;
if (!listenerCountResult.WasSuccessful)
return Result.Failure<IImport>(input, "Invalid export directive", ["Expected listener count"]);
if (!uint.TryParse(listenerCountResult.Value, out var listenerCount))
return Result.Failure<IImport>(input, "Invalid export directive", ["Expected export listener count to be an unsigned integer"]);
var baseTypeNameResult = AlphanumericText.Token()(input);
input = baseTypeNameResult.Remainder;
if (!baseTypeNameResult.WasSuccessful)
return Result.Failure<IImport>(input, "Invalid export directive", ["Expected base type name"]);
var labelPrefix = labelPrefixResult.Value;
var baseTypeName = baseTypeNameResult.Value;
directive = new ExportDirective(labelPrefix, listenerCount, baseTypeName)
{ {
Line = line, Line = line,
Column = column, Column = column,
+2
View File
@@ -10,6 +10,8 @@ public static partial class Lexer
public static readonly Parser<string> WordText = Parse.Letter.AtLeastOnce().Text(); public static readonly Parser<string> WordText = Parse.Letter.AtLeastOnce().Text();
public static readonly Parser<string> WholeNumber = Parse.Digit.AtLeastOnce().Text();
public static readonly Parser<string> Uri = Parse.LetterOrDigit.Or(Parse.Chars(":/!._-")).AtLeastOnce().Text(); public static readonly Parser<string> Uri = Parse.LetterOrDigit.Or(Parse.Chars(":/!._-")).AtLeastOnce().Text();
public static readonly Parser<string> StatementEnd = Parse.Char(';').Return(";").Or(Parse.LineTerminator); public static readonly Parser<string> StatementEnd = Parse.Char(';').Return(";").Or(Parse.LineTerminator);
+21 -8
View File
@@ -14,20 +14,33 @@ public record SectionDirective : Directive, IBodyItem
public record ExportDirective : Directive public record ExportDirective : Directive
{ {
public ExportDirective(string labelPrefix) : base("export") public ExportDirective(string labelPrefix, uint listenerCount, string baseTypeName) : base("export")
{ {
LabelPrefix = labelPrefix; LabelPrefix = labelPrefix;
ListenerCount = listenerCount;
BaseTypeName = baseTypeName;
} }
public string LabelPrefix { get; init; } public string LabelPrefix { get; init; }
public override string ToString() => $"{base.ToString()} {LabelPrefix}"; public uint ListenerCount { get; init; }
public string InitializePropertiesLabel => LabelPrefix + "_prop"; public string BaseTypeName { get; init; }
public string InitializeLocalInputLabel => LabelPrefix + "_locl";
public string InitializeContentLabel => LabelPrefix + "_cont";
public string InitialEvaluateOffsetsLabelPrefix => LabelPrefix + "_evali_"; public override string ToString() => $"{base.ToString()} {LabelPrefix} {ListenerCount} {BaseTypeName}";
public string FinalEvaluateOffsetsLabelPrefix => LabelPrefix + "_evalf_";
public string RefreshGroupOffsetsLabelPrefix => LabelPrefix + "_rfsh_"; public string InitializePropertiesLabel => GetInitializePropertiesLabel(LabelPrefix);
public string InitializeLocalsInputLabel => GetInitializeLocalsInputLabel(LabelPrefix);
public string InitializeContentLabel => GetInitializeContentLabel(LabelPrefix);
public string InitialEvaluateOffsetsLabelPrefix => GetInitialEvaluateOffsetsLabelPrefix(LabelPrefix);
public string FinalEvaluateOffsetsLabelPrefix => GetInitialEvaluateOffsetsLabelPrefix(LabelPrefix);
public string RefreshGroupOffsetsLabelPrefix => GetRefreshGroupOffsetsLabelPrefix(LabelPrefix);
public static string GetInitializePropertiesLabel(string prefix) => prefix + "_prop";
public static string GetInitializeLocalsInputLabel(string prefix) => prefix + "_locl";
public static string GetInitializeContentLabel(string prefix) => prefix + "_cont";
public static string GetInitialEvaluateOffsetsLabelPrefix(string prefix) => prefix + "_evali_";
public static string GetFinalEvaluateOffsetsLabelPrefix(string prefix) => prefix + "_evalf_";
public static string GetRefreshGroupOffsetsLabelPrefix(string prefix) => prefix + "_rfsh_";
} }