Files
ZuneUIXTools/libs/UIX.Asm/Disassembler.cs
T

279 lines
13 KiB
C#
Raw Normal View History

2024-01-30 23:30:42 -06:00
using Humanizer;
2024-01-31 09:21:30 -06:00
using Microsoft.Iris.Asm.Models;
2024-01-30 23:30:42 -06:00
using Microsoft.Iris.Markup;
using System;
using System.Collections.Generic;
2024-02-08 13:32:44 -06:00
using System.Linq;
2024-01-30 23:30:42 -06:00
namespace Microsoft.Iris.Asm;
public class Disassembler
{
private readonly MarkupLoadResult _loadResult;
2024-02-08 13:32:44 -06:00
private readonly Dictionary<string, string> _importedUris;
private readonly Dictionary<uint, List<Label>> _offsetLabelMap = new();
2024-01-30 23:30:42 -06:00
private Disassembler(MarkupLoadResult loadResult)
{
_loadResult = loadResult;
2024-02-08 13:32:44 -06:00
_importedUris = new()
{
[_loadResult.Uri] = "me",
["http://schemas.microsoft.com/2007/uix"] = null
};
2024-01-30 23:30:42 -06:00
}
public static Disassembler Load(MarkupLoadResult loadResult) => new(loadResult);
2024-02-08 13:32:44 -06:00
public IEnumerable<Directive> GetExports()
{
foreach (var typeSchema in _loadResult.ExportTable)
{
var labelPrefix = typeSchema.Name;
if (typeSchema is not MarkupTypeSchema markupTypeSchema)
throw new Exception($"Disassembler failed to disassemble export {typeSchema}, '{typeSchema.GetType()}' is not supported.");
2024-02-09 09:18:50 -06:00
var baseName = markupTypeSchema.MarkupType.ToString();
yield return new ExportDirective(labelPrefix, markupTypeSchema.ListenerCount, baseName);
2024-02-08 13:32:44 -06:00
var propOffset = markupTypeSchema.InitializePropertiesOffset;
if (propOffset != uint.MaxValue)
2024-02-08 13:32:44 -06:00
InsertLabel(propOffset, ExportDirective.GetInitializePropertiesLabel(labelPrefix));
var loclOffset = markupTypeSchema.InitializeLocalsInputOffset;
if (loclOffset != uint.MaxValue)
2024-02-08 13:32:44 -06:00
InsertLabel(loclOffset, ExportDirective.GetInitializeLocalsInputLabel(labelPrefix));
var contOffset = markupTypeSchema.InitializeContentOffset;
if (contOffset != uint.MaxValue)
2024-02-08 13:32:44 -06:00
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);
}
}
}
}
2024-01-30 23:30:42 -06:00
public IEnumerable<IImport> GetImports()
{
2024-02-08 13:32:44 -06:00
// Ues _importedUris to keep track of what has already been imported.
// Skip self and default UIX namespace.
2024-02-07 09:42:18 -06:00
2024-01-30 23:30:42 -06:00
foreach (var typeImport in _loadResult.ImportTables.TypeImports)
{
var uri = typeImport.Owner.Uri;
2024-02-08 13:32:44 -06:00
if (!_importedUris.TryGetValue(uri, out var namespacePrefix))
2024-02-08 10:52:01 -06:00
{
namespacePrefix = uri;
2024-02-08 13:32:44 -06:00
var schemeLength = uri.IndexOf("://");
if (schemeLength > 0)
2024-01-30 23:30:42 -06:00
{
2024-02-08 13:32:44 -06:00
var scheme = uri[..schemeLength];
if (scheme == "assembly")
{
// Assume the URI represents a C# namespace
2024-02-08 10:52:01 -06:00
namespacePrefix = uri.Split('.', '/', '\\', '!')[^1];
2024-02-08 13:32:44 -06:00
// Remove the extra assembly info
uri = uri.Split(',')[0];
}
else
{
// Assume the URI represents a file,
// skip the extension
2024-02-08 10:52:01 -06:00
namespacePrefix = uri.Split('.', '/', '\\', '!')[^2];
}
2024-01-30 23:30:42 -06:00
}
2024-02-08 10:52:01 -06:00
namespacePrefix = namespacePrefix.Camelize();
2024-02-08 13:32:44 -06:00
_importedUris.Add(uri, namespacePrefix);
2024-02-08 10:52:01 -06:00
yield return new NamespaceImport(uri, namespacePrefix);
2024-01-30 23:30:42 -06:00
}
2024-02-08 10:52:01 -06:00
yield return new TypeImport(namespacePrefix, typeImport.Name);
2024-01-30 23:30:42 -06:00
};
}
public IEnumerable<IBodyItem> GetBody()
{
var reader = _loadResult.ObjectSection;
2024-01-31 19:44:23 -06:00
// Insert a label to mark the start of the object section.
yield return new SectionDirective("object");
2024-01-31 19:44:23 -06:00
2024-01-30 23:30:42 -06:00
while (reader.CurrentOffset < reader.Size)
{
2024-02-08 13:32:44 -06:00
if (_offsetLabelMap.TryGetValue(reader.CurrentOffset, out var labels))
foreach (var label in labels)
yield return label;
2024-01-30 23:30:42 -06:00
var opCode = (OpCode)reader.ReadByte();
switch (opCode)
{
2024-01-31 18:34:45 -06:00
// CMD (No operands)
case OpCode.InitializeInstanceIndirect: // INITI
case OpCode.PushNull: // PSHN
case OpCode.PushThis: // PSHT
case OpCode.DiscardValue: // DIS
case OpCode.ReturnValue: // RET
case OpCode.ReturnVoid: // RETV
yield return Instruction.CreateWithSchema(opCode);
2024-01-31 18:34:45 -06:00
break;
// CMD <UInt16>
case OpCode.ConstructObject: // COBJ <typeIndex>
case OpCode.ConstructObjectIndirect: // COBJI <assignmentTypeIndex>
case OpCode.InitializeInstance: // INIT <typeIndex>
case OpCode.LookupSymbol: // LSYM <symbolRefIndex>
case OpCode.WriteSymbol: // WSYM <symbolRefIndex>
case OpCode.WriteSymbolPeek: // WSYMP <symbolRefIndex>
case OpCode.ClearSymbol: // CSYM <symbolRefIndex>
case OpCode.PropertyInitialize: // PINI <propertyIndex>
case OpCode.PropertyInitializeIndirect: // PINII <propertyIndex>
case OpCode.PropertyListAdd: // PLAD <propertyIndex>
case OpCode.PropertyAssign: // PASS <propertyIndex>
case OpCode.PropertyAssignStatic: // PASST <propertyIndex>
case OpCode.PropertyGet: // PGET <propertyIndex>
case OpCode.PropertyGetPeek: // PGETP <propertyIndex>
case OpCode.PropertyGetStatic: // PGETT <propertyIndex>
case OpCode.MethodInvoke: // MINV <methodIndex>
case OpCode.MethodInvokePeek: // MINVP <methodIndex>
case OpCode.MethodInvokeStatic: // MINVT <methodIndex>
case OpCode.MethodInvokePushLastParam: // MINVA <methodIndex>
case OpCode.MethodInvokeStaticPushLastParam: // MINVAT <methodIndex>
case OpCode.VerifyTypeCast: // VTC <typeIndex>
case OpCode.IsCheck: // ISC <targetTypeIndex>
case OpCode.As: // ASC <targetTypeIndex>
case OpCode.TypeOf: // TYP <typeIndex>
case OpCode.PushConstant: // PSHC <constantIndex>
case OpCode.ConstructListenerStorage: // CLIS <listenerCount>
yield return Instruction.CreateWithSchema(opCode, reader.ReadUInt16());
2024-01-30 23:30:42 -06:00
break;
2024-01-31 18:34:45 -06:00
// CMD <UInt32>
case OpCode.JumpIfFalse: // JMPF <jumpTo>
case OpCode.JumpIfFalsePeek: // JMPFP <jumpTo>
case OpCode.JumpIfTruePeek: // JMPT <jumpTo>
case OpCode.JumpIfNullPeek: // JMPNP <jumpTo>
case OpCode.Jump: // JMP <jumpTo>
yield return Instruction.CreateWithSchema(opCode, reader.ReadUInt32());
2024-01-30 23:30:42 -06:00
break;
2024-01-31 18:34:45 -06:00
// CMD <Int32>
case OpCode.EnterDebugState: // DBG <breakpointIndex>
yield return Instruction.CreateWithSchema(opCode, reader.ReadInt32());
2024-01-31 18:34:45 -06:00
break;
// CMD <UInt16> <UInt16>
case OpCode.ConstructObjectParam: // COBP <targetTypeIndex> <constructorIndex>
case OpCode.ConstructFromString: // CSTR <typeIndex> <stringIndex>
case OpCode.PropertyDictionaryAdd: // PDAD <propertyIndex> <keyIndex>
case OpCode.ConvertType: // CON <toTypeIndex> <fromTypeIndex>
yield return Instruction.CreateWithSchema(opCode, reader.ReadUInt16(), reader.ReadUInt16());
2024-01-30 23:30:42 -06:00
break;
2024-01-31 18:34:45 -06:00
case OpCode.JumpIfDictionaryContains: // JMPD <propertyIndex> <keyIndex> <jumpTo>
yield return Instruction.CreateWithSchema(opCode, reader.ReadUInt16(), reader.ReadUInt16(), reader.ReadUInt32());
2024-01-30 23:30:42 -06:00
break;
2024-01-31 18:34:45 -06:00
case OpCode.ConstructFromBinary: // CBIN <typeIndex> <object>
2024-01-30 23:30:42 -06:00
var cbinTypeIndex = reader.ReadUInt16();
TypeSchema cbinTypeSchema = _loadResult.ImportTables.TypeImports[cbinTypeIndex];
object cbinObject = cbinTypeSchema.DecodeBinary(reader);
yield return Instruction.CreateWithSchema(opCode, cbinTypeIndex, cbinObject);
2024-01-30 23:30:42 -06:00
break;
2024-01-31 18:34:45 -06:00
case OpCode.Operation: // OPR <opHostIndex> <operation>
var opHostIndex = reader.ReadUInt16();
var op = (OperationType)reader.ReadByte();
2024-01-30 23:30:42 -06:00
yield return Instruction.CreateWithSchema(opCode, opHostIndex, op);
2024-01-30 23:30:42 -06:00
break;
2024-01-31 18:34:45 -06:00
case OpCode.Listen: // LIS <listenerIndex> <listenerType> <watchIndex> <handlerOffset>
case OpCode.DestructiveListen: // LISD <listenerIndex> <listenerType> <watchIndex> <handlerOffset> <refreshOffset>
var listenerIndex = reader.ReadUInt16();
var listenerType = reader.ReadByte();
var watchIndex = reader.ReadUInt16();
var handlerOffset = reader.ReadUInt32();
2024-01-31 18:34:45 -06:00
if (opCode == OpCode.DestructiveListen)
yield return Instruction.CreateWithSchema(opCode, listenerIndex, listenerType, watchIndex, handlerOffset, reader.ReadUInt32());
else
yield return Instruction.CreateWithSchema(opCode, listenerIndex, listenerType, watchIndex, handlerOffset);
2024-01-31 18:34:45 -06:00
2024-01-30 23:30:42 -06:00
break;
2024-01-31 18:34:45 -06:00
default:
throw new NotImplementedException($"The {opCode} instruction has not been implemented yet.");
2024-01-30 23:30:42 -06:00
}
}
yield break;
}
public string Write()
{
_loadResult.Load(LoadPass.DeclareTypes);
_loadResult.Load(LoadPass.PopulatePublicModel);
_loadResult.Load(LoadPass.Full);
_loadResult.Load(LoadPass.Done);
2024-02-08 13:32:44 -06:00
List<IDirective> directives = GetImports().Cast<IDirective>()
.Concat(GetExports())
.ToList();
2024-01-30 23:30:42 -06:00
List<IBodyItem> body = new(GetBody());
2024-02-08 13:32:44 -06:00
Program asmProgram = new(directives, body);
2024-01-30 23:30:42 -06:00
return asmProgram.ToString();
}
2024-02-08 13:32:44 -06:00
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));
}
2024-01-30 23:30:42 -06:00
}