From 8ced3e771a297e34f90d74e9e58a703e8921a979 Mon Sep 17 00:00:00 2001 From: Yoshi Askharoun Date: Sun, 11 Feb 2024 20:39:07 -0600 Subject: [PATCH] Add constants to disassembly --- libs/UIX.Asm/Disassembler.cs | 184 +++++++++++++++-------------------- 1 file changed, 81 insertions(+), 103 deletions(-) diff --git a/libs/UIX.Asm/Disassembler.cs b/libs/UIX.Asm/Disassembler.cs index 668bcf0..bd6b739 100644 --- a/libs/UIX.Asm/Disassembler.cs +++ b/libs/UIX.Asm/Disassembler.cs @@ -128,10 +128,53 @@ public class Disassembler yield return new NamespaceImport(uri, namespacePrefix); } - yield return new TypeImport(namespacePrefix, typeImport.Name); + yield return new TypeImport(new(namespacePrefix, typeImport.Name)); }; } + public IEnumerable GetConstants() + { + var constantsTable = _loadResult.ConstantsTable; + + bool canUsePersistList = constantsTable.PersistList is not null; + if (canUsePersistList) + { + var constants = _loadResult.ConstantsTable.PersistList; + + for (int c = 0; c < constants.Length; c++) + { + var persistedConstant = constants[c]; + + var typeSchema = persistedConstant.Type; + var qualifiedTypeName = GetQualifiedName(typeSchema); + + yield return new ConstantDirective($"const{c:D}", qualifiedTypeName, persistedConstant.Data.ToString()); + } + } + else + { + // UIB doesn't persist constants, so we have to use an alternate, slower method + for (int c = 0; ; c++) + { + object constantValue; + try + { + constantValue = constantsTable.Get(c); + } + catch + { + break; + } + + var runtimeType = constantValue.GetType(); + var typeSchema = _loadResult.ImportTables.TypeImports.FirstOrDefault(t => t.RuntimeType == runtimeType); + + QualifiedTypeName qualifiedTypeName = GetQualifiedName(typeSchema); + yield return new ConstantDirective($"const{c:D}", qualifiedTypeName, constantValue.ToString()); + } + } + } + public IEnumerable GetCode() { var reader = _loadResult.ObjectSection; @@ -146,107 +189,40 @@ public class Disassembler yield return label; var opCode = (OpCode)reader.ReadByte(); - - switch (opCode) + + var instSchema = InstructionSet.InstructionSchema[opCode]; + Operand[] operands = new Operand[instSchema.Length]; + + for (int i = 0; i < instSchema.Length; i++) { - // 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); - break; + var operandDataType = instSchema[i]; + Operand operand; - // 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 Instruction.CreateWithSchema(opCode, reader.ReadUInt16()); - break; + if (operandDataType == LiteralDataType.ConstantIndex) + { + // Refer to the constant by name rather than index + var constantIndex = reader.ReadUInt16(); - // CMD - case OpCode.JumpIfFalse: // JMPF - case OpCode.JumpIfFalsePeek: // JMPFP - case OpCode.JumpIfTruePeek: // JMPT - case OpCode.JumpIfNullPeek: // JMPNP - case OpCode.Jump: // JMP - yield return Instruction.CreateWithSchema(opCode, reader.ReadUInt32()); - break; + operand = new OperandReference($"const{constantIndex}"); + } + else + { + object literalValue = OperandLiteral.ReduceDataType(operandDataType) switch + { + LiteralDataType.Byte => reader.ReadByte(), + LiteralDataType.UInt16 => reader.ReadUInt16(), + LiteralDataType.UInt32 => reader.ReadUInt32(), + LiteralDataType.Int32 => reader.ReadInt32(), + _ => throw new InvalidOperationException($"Unexpected operand data type '{operandDataType}'") + }; - // CMD - case OpCode.EnterDebugState: // DBG - yield return Instruction.CreateWithSchema(opCode, reader.ReadInt32()); - break; + operand = new OperandLiteral(literalValue, operandDataType); + } - // CMD - case OpCode.ConstructObjectParam: // COBP - case OpCode.ConstructFromString: // CSTR - case OpCode.PropertyDictionaryAdd: // PDAD - case OpCode.ConvertType: // CON - yield return Instruction.CreateWithSchema(opCode, reader.ReadUInt16(), reader.ReadUInt16()); - break; - - case OpCode.JumpIfDictionaryContains: // JMPD - yield return Instruction.CreateWithSchema(opCode, reader.ReadUInt16(), reader.ReadUInt16(), reader.ReadUInt32()); - break; - - case OpCode.ConstructFromBinary: // CBIN - var cbinTypeIndex = reader.ReadUInt16(); - TypeSchema cbinTypeSchema = _loadResult.ImportTables.TypeImports[cbinTypeIndex]; - object cbinObject = cbinTypeSchema.DecodeBinary(reader); - - yield return Instruction.CreateWithSchema(opCode, cbinTypeIndex, cbinObject); - break; - - case OpCode.Operation: // OPR - var opHostIndex = reader.ReadUInt16(); - var op = (OperationType)reader.ReadByte(); - - yield return Instruction.CreateWithSchema(opCode, opHostIndex, op); - break; - - case OpCode.Listen: // LIS - case OpCode.DestructiveListen: // LISD - var listenerIndex = reader.ReadUInt16(); - var listenerType = reader.ReadByte(); - var watchIndex = reader.ReadUInt16(); - var handlerOffset = reader.ReadUInt32(); - - 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); - - break; - - default: - throw new NotImplementedException($"The {opCode} instruction has not been implemented yet."); + operands[i] = operand; } + + yield return new Instruction(opCode, operands); } yield break; @@ -259,23 +235,25 @@ public class Disassembler _loadResult.Load(LoadPass.Full); _loadResult.Load(LoadPass.Done); - IEnumerable> segments = [ + List> segments = [ GetExports(), GetImports(), + GetConstants(), GetCode(), ]; - Program asmProgram = new(segments.SelectMany(e => e)); + List body = []; + foreach (var segment in segments) + body.AddRange(segment); + + Program asmProgram = new(body); return asmProgram.ToString(); } - private string GetQualifiedName(TypeSchema schema) + private QualifiedTypeName GetQualifiedName(TypeSchema schema) { _importedUris.TryGetValue(schema.Owner.Uri, out string prefix); - - return prefix != null - ? $"{prefix}:{schema.Name}" - : schema.Name; + return new(prefix, schema.Name); } private void InsertLabel(uint offset, string labelName)