[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.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<Instruction>()
.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;
+26 -20
View File
@@ -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<StatementSyntax> 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<StatementSyntax> statements)
public static SyntaxTree CreateTree(IEnumerable<StatementSyntax> 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<StatementSyntax> 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<object> stack, ControlFlowAnalyzer cfa = null)
+8 -2
View File
@@ -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<object> 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: