using Humanizer; using Microsoft.Iris.Asm.Models; using Microsoft.Iris.Markup; using System; using System.Collections.Generic; namespace Microsoft.Iris.Asm; public class Disassembler { private readonly MarkupLoadResult _loadResult; private Disassembler(MarkupLoadResult loadResult) { _loadResult = loadResult; } public static Disassembler Load(MarkupLoadResult loadResult) => new(loadResult); public IEnumerable GetImports() { foreach (var typeImport in _loadResult.ImportTables.TypeImports) { var uri = typeImport.Owner.Uri; // Skip default UIX namespace if (uri == "http://schemas.microsoft.com/2007/uix") continue; // Skip self if (uri == _loadResult.Uri) continue; string name = uri; var schemeLength = uri.IndexOf("://"); if (schemeLength > 0) { var scheme = uri[..schemeLength]; if (scheme == "assembly") { // Assume the URI represents a C# namespace name = uri.Split('.', '/', '\\')[^1]; } else { // Assume the URI represents a file, // skip the extension name = uri.Split('.', '/', '\\')[^2]; } } yield return new NamespaceImport(uri, name.Camelize()); }; } public IEnumerable GetBody() { var reader = _loadResult.ObjectSection; // Insert a label to mark the start of the object section. // In the future, this might use a special directive like `.section object` yield return new Label("code"); while (reader.CurrentOffset < reader.Size) { var opCode = (OpCode)reader.ReadByte(); switch (opCode) { // 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 new Instruction(opCode, []); break; // CMD case OpCode.ConstructObject: // COBJ case OpCode.ConstructObjectIndirect: // COBJI case OpCode.InitializeInstance: // INIT case OpCode.LookupSymbol: // LSYM case OpCode.WriteSymbol: // WSYM case OpCode.WriteSymbolPeek: // WSYMP case OpCode.ClearSymbol: // CSYM case OpCode.PropertyInitialize: // PINI case OpCode.PropertyInitializeIndirect: // PINII case OpCode.PropertyListAdd: // PLAD case OpCode.PropertyAssign: // PASS case OpCode.PropertyAssignStatic: // PASST case OpCode.PropertyGet: // PGET case OpCode.PropertyGetPeek: // PGETP case OpCode.PropertyGetStatic: // PGETT case OpCode.MethodInvoke: // MINV case OpCode.MethodInvokePeek: // MINVP case OpCode.MethodInvokeStatic: // MINVT case OpCode.MethodInvokePushLastParam: // MINVA case OpCode.MethodInvokeStaticPushLastParam: // MINVAT case OpCode.VerifyTypeCast: // VTC case OpCode.IsCheck: // ISC case OpCode.As: // ASC case OpCode.TypeOf: // TYP case OpCode.PushConstant: // PSHC case OpCode.ConstructListenerStorage: // CLIS yield return new Instruction(opCode, [new(reader.ReadUInt16())]); break; // CMD case OpCode.JumpIfFalse: // JMPF case OpCode.JumpIfFalsePeek: // JMPFP case OpCode.JumpIfTruePeek: // JMPT case OpCode.JumpIfNullPeek: // JMPNP case OpCode.Jump: // JMP yield return new Instruction(opCode, [new(reader.ReadUInt32())]); break; // CMD case OpCode.EnterDebugState: // DBG yield return new Instruction(opCode, [new(reader.ReadInt32())]); break; // CMD case OpCode.ConstructObjectParam: // COBP case OpCode.ConstructFromString: // CSTR case OpCode.PropertyDictionaryAdd: // PDAD case OpCode.ConvertType: // CON yield return new Instruction(opCode, [new(reader.ReadUInt16()), new(reader.ReadUInt16())]); break; case OpCode.JumpIfDictionaryContains: // JMPD yield return new Instruction(opCode, [new(reader.ReadUInt16()), new(reader.ReadUInt16()), new(reader.ReadUInt32())]); break; case OpCode.ConstructFromBinary: // CBIN var cbinTypeIndex = reader.ReadUInt16(); TypeSchema cbinTypeSchema = _loadResult.ImportTables.TypeImports[cbinTypeIndex]; object cbinObject = cbinTypeSchema.DecodeBinary(reader); yield return new Instruction(opCode, [new(cbinTypeIndex), new(cbinObject)]); break; case OpCode.Operation: // OPR var opHostIndex = reader.ReadUInt16(); var op = (OperationType)reader.ReadByte(); yield return new Instruction(opCode, op, [new(opHostIndex)]); break; case OpCode.Listen: // LIS case OpCode.DestructiveListen: // LISD List lisOperands = [new(reader.ReadUInt16()), new(reader.ReadByte()), new(reader.ReadUInt16()), new(reader.ReadUInt32())]; if (opCode == OpCode.DestructiveListen) lisOperands.Add(new(reader.ReadUInt32())); yield return new Instruction(opCode, lisOperands); break; default: throw new NotImplementedException($"The {opCode} instruction has not been implemented yet."); } } yield break; } public string Write() { _loadResult.Load(LoadPass.DeclareTypes); _loadResult.Load(LoadPass.PopulatePublicModel); _loadResult.Load(LoadPass.Full); _loadResult.Load(LoadPass.Done); List imports = new(GetImports()); List body = new(GetBody()); Program asmProgram = new(imports, body); return asmProgram.ToString(); } }