[WIP] Build source map for scripts

This commit is contained in:
Yoshi Askharoun
2025-09-27 03:08:25 -05:00
parent bbf9a6b87b
commit 97c6e7d1c2
4 changed files with 45 additions and 24 deletions
+9
View File
@@ -2,6 +2,7 @@
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.Iris.Asm; using Microsoft.Iris.Asm;
using Microsoft.Iris.Asm.Models; using Microsoft.Iris.Asm.Models;
using Microsoft.Iris.Debug.Symbols;
using Microsoft.Iris.Markup; using Microsoft.Iris.Markup;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -58,6 +59,12 @@ internal class DecompileContext
_scriptMap = []; _scriptMap = [];
DebugSymbols = new()
{
CompiledFileName = _loadResult.ErrorContextUri,
ScriptSymbols = [],
};
_instructions = ObjectSection.Decode(_loadResult.ObjectSection) _instructions = ObjectSection.Decode(_loadResult.ObjectSection)
.OfType<Instruction>() .OfType<Instruction>()
.ToArray(); .ToArray();
@@ -71,6 +78,8 @@ internal class DecompileContext
public MarkupImportTables ImportTables => _loadResult.ImportTables; public MarkupImportTables ImportTables => _loadResult.ImportTables;
public FileDebugSymbols DebugSymbols { get; }
[MemberNotNullWhen(true, nameof(_dataTableLoadResult))] [MemberNotNullWhen(true, nameof(_dataTableLoadResult))]
private bool UseSharedDataTable => _dataTableLoadResult is not null; private bool UseSharedDataTable => _dataTableLoadResult is not null;
+26 -20
View File
@@ -3,6 +3,7 @@ using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Iris.Asm.Models; using Microsoft.Iris.Asm.Models;
using Microsoft.Iris.Debug.Symbols;
using Microsoft.Iris.DecompXml.Mock; using Microsoft.Iris.DecompXml.Mock;
using Microsoft.Iris.Markup; using Microsoft.Iris.Markup;
using Microsoft.Iris.Markup.UIX; using Microsoft.Iris.Markup.UIX;
@@ -23,7 +24,7 @@ partial class Decompiler
private SyntaxTree DecompileScript(uint startOffset, MarkupTypeSchema export) private SyntaxTree DecompileScript(uint startOffset, MarkupTypeSchema export)
{ {
var statements = DecompileMethod(startOffset, export); var statements = DecompileMethod(startOffset, export);
return CreateTree(statements); return CreateTree(statements, startOffset);
} }
public List<StatementSyntax> DecompileMethod(uint startOffset, MarkupTypeSchema export) public List<StatementSyntax> DecompileMethod(uint startOffset, MarkupTypeSchema export)
@@ -501,7 +502,7 @@ partial class Decompiler
if (statements is null) if (statements is null)
break; break;
scriptContent = CreateTree(statements); scriptContent = CreateTree(statements, scriptOffset);
_context.SetScriptContent(initType, scriptOffset, scriptContent); _context.SetScriptContent(initType, scriptOffset, scriptContent);
} }
@@ -595,26 +596,29 @@ partial class Decompiler
return newNode; return newNode;
} }
public static SyntaxTree CreateTree(IEnumerable<StatementSyntax> statements) public static SyntaxTree CreateTree(IEnumerable<StatementSyntax> statements, uint? startOffset = null)
{ {
return SyntaxTree( var root = CompilationUnit()
CompilationUnit().WithMembers( .WithMembers(
[.. statements.Select(GlobalStatement)] [.. 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<StatementSyntax> statements, CancellationToken token = default) => FormatScript(CreateTree(statements), token); public string FormatSyntaxNode(SyntaxNode root, CancellationToken cancellationToken = default)
public static string FormatSyntaxNode(SyntaxNode root, CancellationToken cancellationToken = default)
{ {
Debug.Symbols.ScriptDebugSymbols symbols = new() uint? scriptOffset = null;
ScriptDebugSymbols debugSymbols = new()
{ {
LineNumberMap = [] SourceMap = []
}; };
root = root.NormalizeWhitespace(); root = root.NormalizeWhitespace();
@@ -626,20 +630,22 @@ partial class Decompiler
var location = node.GetLocation(); 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 var sourceText = root
.SyntaxTree .SyntaxTree
.GetText(cancellationToken); .GetText(cancellationToken);
foreach (var mapping in symbols.LineNumberMap.OrderBy(x => x.Key)) debugSymbols.SourceCode = sourceText.ToString();
{
var subtext = sourceText.GetSubText(CodeAnalysis.Text.TextSpan.FromBounds(mapping.Value.Start, mapping.Value.End));
System.Diagnostics.Debug.WriteLine($"0x{mapping.Key:X}: `{subtext}`");
}
return sourceText.ToString(); if (scriptOffset is not null)
_context.DebugSymbols.ScriptSymbols[scriptOffset.Value] = debugSymbols;
return debugSymbols.SourceCode;
} }
private bool TryDecompileExpression(Instruction instruction, Stack<object> stack, ControlFlowAnalyzer cfa = null) private bool TryDecompileExpression(Instruction instruction, Stack<object> stack, ControlFlowAnalyzer cfa = null)
+8 -2
View File
@@ -1,6 +1,7 @@
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Iris.Asm; using Microsoft.Iris.Asm;
using Microsoft.Iris.Debug.Symbols;
using Microsoft.Iris.DecompXml.Mock; using Microsoft.Iris.DecompXml.Mock;
using Microsoft.Iris.Markup; using Microsoft.Iris.Markup;
using System; using System;
@@ -87,7 +88,7 @@ public partial class Decompiler
{ {
var statements = DecompileMethod(offset, export); var statements = DecompileMethod(offset, export);
AddMethodAttribute(statements, "FinalEvaluate"); AddMethodAttribute(statements, "FinalEvaluate");
_context.SetScriptContent(export, offset, CreateTree(statements)); _context.SetScriptContent(export, offset, CreateTree(statements, offset));
} }
foreach (var offset in export.RefreshGroupOffsets ?? []) foreach (var offset in export.RefreshGroupOffsets ?? [])
@@ -161,6 +162,8 @@ public partial class Decompiler
return sb.ToString(); return sb.ToString();
} }
public FileDebugSymbols DebugSymbols => _context.DebugSymbols;
private Stack<object> AnalyzeMethodForInit(uint startOffset, XElement elemToInit, MarkupTypeSchema initType, string methodName = "") private Stack<object> AnalyzeMethodForInit(uint startOffset, XElement elemToInit, MarkupTypeSchema initType, string methodName = "")
{ {
var methodBody = _context.GetMethodBody(startOffset).ToArray(); var methodBody = _context.GetMethodBody(startOffset).ToArray();
@@ -283,7 +286,10 @@ public partial class Decompiler
// Ensure stack is in valid state // Ensure stack is in valid state
var isPeek = instruction.OpCode is OpCode.JumpIfFalsePeek or OpCode.JumpIfTruePeek or OpCode.JumpIfNullPeek; 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; break;
case OpCode.ConstructObjectParam: case OpCode.ConstructObjectParam: