From 97c6e7d1c24b3cacaf89eb8e2388740130587077 Mon Sep 17 00:00:00 2001 From: Yoshi Askharoun Date: Sat, 27 Sep 2025 03:08:25 -0500 Subject: [PATCH] [WIP] Build source map for scripts --- libs/MicrosoftIris | 2 +- libs/UIX.DecompXml/DecompileContext.cs | 9 +++++ libs/UIX.DecompXml/Decompiler.Script.cs | 48 ++++++++++++++----------- libs/UIX.DecompXml/Decompiler.cs | 10 ++++-- 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/libs/MicrosoftIris b/libs/MicrosoftIris index 3307b6e..55815fe 160000 --- a/libs/MicrosoftIris +++ b/libs/MicrosoftIris @@ -1 +1 @@ -Subproject commit 3307b6ea3c1a29aab7af8c6c97f2ca40b401db7b +Subproject commit 55815fe59d1473c620da082573bd6656139c0030 diff --git a/libs/UIX.DecompXml/DecompileContext.cs b/libs/UIX.DecompXml/DecompileContext.cs index 92def5b..e89d9a9 100644 --- a/libs/UIX.DecompXml/DecompileContext.cs +++ b/libs/UIX.DecompXml/DecompileContext.cs @@ -2,6 +2,7 @@ using Microsoft.CodeAnalysis; using Microsoft.Iris.Asm; using Microsoft.Iris.Asm.Models; +using Microsoft.Iris.Debug.Symbols; using Microsoft.Iris.Markup; using System; using System.Collections.Generic; @@ -58,6 +59,12 @@ internal class DecompileContext _scriptMap = []; + DebugSymbols = new() + { + CompiledFileName = _loadResult.ErrorContextUri, + ScriptSymbols = [], + }; + _instructions = ObjectSection.Decode(_loadResult.ObjectSection) .OfType() .ToArray(); @@ -71,6 +78,8 @@ internal class DecompileContext public MarkupImportTables ImportTables => _loadResult.ImportTables; + public FileDebugSymbols DebugSymbols { get; } + [MemberNotNullWhen(true, nameof(_dataTableLoadResult))] private bool UseSharedDataTable => _dataTableLoadResult is not null; diff --git a/libs/UIX.DecompXml/Decompiler.Script.cs b/libs/UIX.DecompXml/Decompiler.Script.cs index 45b07ed..1ccb18b 100644 --- a/libs/UIX.DecompXml/Decompiler.Script.cs +++ b/libs/UIX.DecompXml/Decompiler.Script.cs @@ -3,6 +3,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Iris.Asm.Models; +using Microsoft.Iris.Debug.Symbols; using Microsoft.Iris.DecompXml.Mock; using Microsoft.Iris.Markup; using Microsoft.Iris.Markup.UIX; @@ -23,7 +24,7 @@ partial class Decompiler private SyntaxTree DecompileScript(uint startOffset, MarkupTypeSchema export) { var statements = DecompileMethod(startOffset, export); - return CreateTree(statements); + return CreateTree(statements, startOffset); } public List DecompileMethod(uint startOffset, MarkupTypeSchema export) @@ -501,7 +502,7 @@ partial class Decompiler if (statements is null) break; - scriptContent = CreateTree(statements); + scriptContent = CreateTree(statements, scriptOffset); _context.SetScriptContent(initType, scriptOffset, scriptContent); } @@ -595,26 +596,29 @@ partial class Decompiler return newNode; } - public static SyntaxTree CreateTree(IEnumerable statements) + public static SyntaxTree CreateTree(IEnumerable statements, uint? startOffset = null) { - return SyntaxTree( - CompilationUnit().WithMembers( + var root = CompilationUnit() + .WithMembers( [.. statements.Select(GlobalStatement)] - ) - ); + ); + + if (startOffset is not null) + root = root.WithOffset(startOffset.Value); + + return SyntaxTree(root); } - public static string FormatInlineExpression(ExpressionSyntax expr, CancellationToken token = default) => '{' + FormatSyntaxNode(expr, token) + '}'; + public string FormatInlineExpression(ExpressionSyntax expr, CancellationToken token = default) => '{' + FormatSyntaxNode(expr, token) + '}'; - public static string FormatScript(SyntaxTree tree, CancellationToken token = default) => FormatSyntaxNode(tree.GetRoot(token), token); + public string FormatScript(SyntaxTree tree, CancellationToken token = default) => FormatSyntaxNode(tree.GetRoot(token), token); - public static string FormatScript(IEnumerable statements, CancellationToken token = default) => FormatScript(CreateTree(statements), token); - - public static string FormatSyntaxNode(SyntaxNode root, CancellationToken cancellationToken = default) + public string FormatSyntaxNode(SyntaxNode root, CancellationToken cancellationToken = default) { - Debug.Symbols.ScriptDebugSymbols symbols = new() + uint? scriptOffset = null; + ScriptDebugSymbols debugSymbols = new() { - LineNumberMap = [] + SourceMap = [] }; root = root.NormalizeWhitespace(); @@ -626,20 +630,22 @@ partial class Decompiler var location = node.GetLocation(); - symbols.LineNumberMap[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; } var sourceText = root .SyntaxTree .GetText(cancellationToken); - foreach (var mapping in symbols.LineNumberMap.OrderBy(x => x.Key)) - { - var subtext = sourceText.GetSubText(CodeAnalysis.Text.TextSpan.FromBounds(mapping.Value.Start, mapping.Value.End)); - System.Diagnostics.Debug.WriteLine($"0x{mapping.Key:X}: `{subtext}`"); - } + debugSymbols.SourceCode = sourceText.ToString(); - return sourceText.ToString(); + if (scriptOffset is not null) + _context.DebugSymbols.ScriptSymbols[scriptOffset.Value] = debugSymbols; + + return debugSymbols.SourceCode; } 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 6d79a1f..5f4e0d3 100644 --- a/libs/UIX.DecompXml/Decompiler.cs +++ b/libs/UIX.DecompXml/Decompiler.cs @@ -1,6 +1,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Iris.Asm; +using Microsoft.Iris.Debug.Symbols; using Microsoft.Iris.DecompXml.Mock; using Microsoft.Iris.Markup; using System; @@ -87,7 +88,7 @@ public partial class Decompiler { var statements = DecompileMethod(offset, export); AddMethodAttribute(statements, "FinalEvaluate"); - _context.SetScriptContent(export, offset, CreateTree(statements)); + _context.SetScriptContent(export, offset, CreateTree(statements, offset)); } foreach (var offset in export.RefreshGroupOffsets ?? []) @@ -161,6 +162,8 @@ public partial class Decompiler return sb.ToString(); } + public FileDebugSymbols DebugSymbols => _context.DebugSymbols; + private Stack AnalyzeMethodForInit(uint startOffset, XElement elemToInit, MarkupTypeSchema initType, string methodName = "") { var methodBody = _context.GetMethodBody(startOffset).ToArray(); @@ -283,7 +286,10 @@ public partial class Decompiler // Ensure stack is in valid state var isPeek = instruction.OpCode is OpCode.JumpIfFalsePeek or OpCode.JumpIfTruePeek or OpCode.JumpIfNullPeek; - var rawJumpCondition = IrisExpression.ToSyntax(isPeek ? stack.Peek() : stack.Pop(), _context); + if (isPeek) + stack.Peek(); + else + stack.Pop(); break; case OpCode.ConstructObjectParam: