From 806d697e79969366b008564ed7d355e94077f21f Mon Sep 17 00:00:00 2001 From: "Joshua \"Yoshi\" Askharoun" Date: Fri, 28 Nov 2025 18:06:19 -0600 Subject: [PATCH] XML debug symbols --- libs/MicrosoftIris | 2 +- libs/UIX.DecompXml/DecompileContext.cs | 1 - libs/UIX.DecompXml/Decompiler.Script.cs | 13 +---- libs/UIX.DecompXml/Decompiler.cs | 64 +++++++++++++++++++---- libs/UIX.DecompXml/UIBOffsetAnnotation.cs | 22 ++++++++ 5 files changed, 79 insertions(+), 23 deletions(-) diff --git a/libs/MicrosoftIris b/libs/MicrosoftIris index ae9b24d..2f293ec 160000 --- a/libs/MicrosoftIris +++ b/libs/MicrosoftIris @@ -1 +1 @@ -Subproject commit ae9b24dc34a62d8d4cb4f9c6841dc0f4da25a48f +Subproject commit 2f293ec6dde4bee43e6d1898e12bb5b628e58d29 diff --git a/libs/UIX.DecompXml/DecompileContext.cs b/libs/UIX.DecompXml/DecompileContext.cs index e89d9a9..c81d94a 100644 --- a/libs/UIX.DecompXml/DecompileContext.cs +++ b/libs/UIX.DecompXml/DecompileContext.cs @@ -62,7 +62,6 @@ internal class DecompileContext DebugSymbols = new() { CompiledFileName = _loadResult.ErrorContextUri, - ScriptSymbols = [], }; _instructions = ObjectSection.Decode(_loadResult.ObjectSection) diff --git a/libs/UIX.DecompXml/Decompiler.Script.cs b/libs/UIX.DecompXml/Decompiler.Script.cs index 76a1bd7..e32cb6e 100644 --- a/libs/UIX.DecompXml/Decompiler.Script.cs +++ b/libs/UIX.DecompXml/Decompiler.Script.cs @@ -622,10 +622,6 @@ partial class Decompiler public string FormatSyntaxNode(SyntaxNode root, CancellationToken cancellationToken = default) { uint? scriptOffset = null; - ScriptDebugSymbols debugSymbols = new() - { - SourceMap = [] - }; root = root.NormalizeWhitespace(); @@ -636,7 +632,7 @@ partial class Decompiler var location = node.GetLocation(); - debugSymbols.SourceMap[offset] = new(location.SourceSpan.Start, location.SourceSpan.End); + //debugSymbols.SourceMap[offset] = new(location.SourceSpan.Start, location.SourceSpan.End); if (scriptOffset is null && node is CompilationUnitSyntax) scriptOffset = offset; @@ -646,12 +642,7 @@ partial class Decompiler .SyntaxTree .GetText(cancellationToken); - debugSymbols.SourceCode = sourceText.ToString(); - - if (scriptOffset is not null) - _context.DebugSymbols.ScriptSymbols[scriptOffset.Value] = debugSymbols; - - return debugSymbols.SourceCode; + return sourceText.ToString(); } private bool TryDecompileExpression(Instruction instruction, Stack stack, ControlFlowAnalyzer cfa = null) diff --git a/libs/UIX.DecompXml/Decompiler.cs b/libs/UIX.DecompXml/Decompiler.cs index 5f4e0d3..08f8b27 100644 --- a/libs/UIX.DecompXml/Decompiler.cs +++ b/libs/UIX.DecompXml/Decompiler.cs @@ -143,7 +143,7 @@ public partial class Decompiler return xDoc; } - public string DecompileToSource() + public string DecompileToSource(bool generateDebugSymbols) { var xmlDoc = Decompile(); @@ -159,7 +159,47 @@ public partial class Decompiler { xmlDoc.WriteTo(writer); } - return sb.ToString(); + + var xmlStr = sb.ToString(); + + if (generateDebugSymbols) + PopulateDebugSymbolsWithXml(xmlDoc, xmlStr); + + return xmlStr; + } + + public void PopulateDebugSymbolsWithXml(XDocument xmlDoc, string xmlStr) + { + // Reparse the file to get line info + var xmlDocWithLineInfo = XDocument.Parse(xmlStr, LoadOptions.SetLineInfo); + var lineInfoNodes = xmlDocWithLineInfo.DescendantNodes(); + var offsetInfoNodes = xmlDoc.DescendantNodes(); + + foreach (var (liNode, oiNode) in lineInfoNodes.Zip(offsetInfoNodes, (l, o) => (l, o))) + { + var offsetInfo = oiNode.Annotation(); + if (offsetInfo is null) + continue; + + if (liNode is not IXmlLineInfo lineInfo || !lineInfo.HasLineInfo()) + continue; + + var offset = offsetInfo.Offset; + var start = new SourcePosition(lineInfo.LineNumber, lineInfo.LinePosition); + + var elementStr = oiNode.ToString(); + var endLine = start.Line + elementStr.Count(c => c == '\n'); + var endColumn = elementStr.Length; + var idxLastLine = elementStr.LastIndexOf('\n'); + if (idxLastLine > 0) + endColumn -= idxLastLine; + + var end = new SourcePosition(endLine, endColumn); + + var span = new SourceSpan(start, end); + + DebugSymbols.SourceMap.Xml[offset] = span; + } } public FileDebugSymbols DebugSymbols => _context.DebugSymbols; @@ -189,7 +229,7 @@ public partial class Decompiler case OpCode.ConstructObject: var typeToCtor = _context.GetImportedType(instruction.Operands.ElementAt(0)); - var xObj = new XElement(_context.GetXName(typeToCtor)); + var xObj = new XElement(_context.GetXName(typeToCtor)).WithOffset(instruction); stack.Push(new IrisObject(xObj, typeToCtor)); break; @@ -213,7 +253,8 @@ public partial class Decompiler var target = stack.Pop(); var xTarget = (XElement)ToXmlFriendlyObject(target); - PropertyAssignOnXElement(xTarget, propertyToInit, IrisObject.Create(newPropValue, propertyToInit.PropertyType, _context, initType)); + var xPropSetter = PropertyAssignOnXElement(xTarget, propertyToInit, IrisObject.Create(newPropValue, propertyToInit.PropertyType, _context, initType)); + xPropSetter.SetOffset(instruction); stack.Push(new IrisObject(xTarget, propertyToInit.Owner)); break; @@ -231,10 +272,10 @@ public partial class Decompiler var dictValueObj = IrisObject.Create(dictValue, dictValueType, _context, initType); var targetDictPropertyIndex = (ushort)instruction.Operands.ElementAt(0).Value; - if (targetDictPropertyIndex is ushort.MaxValue) - PropertyDictionaryAddOnXElement(targetInstance, dictValueObj, key); - else - PropertyDictionaryAddOnXElement(targetInstance, _context.ImportTables.PropertyImports[targetDictPropertyIndex], dictValueObj, key); + var xKeyValue = targetDictPropertyIndex is ushort.MaxValue + ? PropertyDictionaryAddOnXElement(targetInstance, dictValueObj, key) + : PropertyDictionaryAddOnXElement(targetInstance, _context.ImportTables.PropertyImports[targetDictPropertyIndex], dictValueObj, key); + xKeyValue.SetOffset(instruction); break; case OpCode.PropertyListAdd: @@ -252,6 +293,7 @@ public partial class Decompiler var targetInstance2 = (XElement)ToXmlFriendlyObject(stack.Peek()); + XObject xListItem; var targetListPropertyIndex = (ushort)instruction.Operands.First().Value; if (targetListPropertyIndex != ushort.MaxValue) { @@ -264,12 +306,14 @@ public partial class Decompiler valueToAddObj = valueToAddObj with { Type = valueToAddType }; } - PropertyListAddOnXElement(targetInstance2, targetListProperty, valueToAddObj); + xListItem = PropertyListAddOnXElement(targetInstance2, targetListProperty, valueToAddObj); } else { - PropertyListAddOnXElement(targetInstance2, valueToAddObj); + xListItem = PropertyListAddOnXElement(targetInstance2, valueToAddObj); } + + xListItem.SetOffset(instruction); break; case OpCode.InitializeInstance: diff --git a/libs/UIX.DecompXml/UIBOffsetAnnotation.cs b/libs/UIX.DecompXml/UIBOffsetAnnotation.cs index 8d18792..2cb6269 100644 --- a/libs/UIX.DecompXml/UIBOffsetAnnotation.cs +++ b/libs/UIX.DecompXml/UIBOffsetAnnotation.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using Microsoft.Iris.Asm.Models; +using System.Xml.Linq; namespace Microsoft.Iris.DecompXml; @@ -23,5 +24,26 @@ internal static class UIBOffsetAnnotation } public static uint GetOffset(SyntaxAnnotation offsetAnnotation) => uint.Parse(offsetAnnotation.Data); + + public static void SetOffset(this XObject xObj, uint offset) => xObj.AddAnnotation(new UIBOffsetXmlAnnotation(offset)); + + public static void SetOffset(this XObject xObj, Instruction instruction) => xObj.SetOffset(instruction.Offset); + + public static XObject WithOffset(this XObject xObj, Instruction instruction) + { + xObj.SetOffset(instruction.Offset); + return xObj; + } + + public static XObject WithNewOffset(this XObject xObj, Instruction instruction) + { + if (xObj.GetOffset() is null) + xObj.SetOffset(instruction.Offset); + return xObj; + } + + public static uint? GetOffset(this XObject xObj) => xObj.Annotation()?.Offset; } +internal record UIBOffsetXmlAnnotation(uint Offset); +