mirror of
https://github.com/ZuneDev/ZuneUIXTools.git
synced 2026-07-27 13:11:59 -07:00
Add type exports to disassembler
This commit is contained in:
+106
-22
@@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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_";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user