[WIP] Class method exports

This commit is contained in:
Yoshi Askharoun
2025-07-21 01:46:38 -05:00
parent e74c47e480
commit 2e8f22b963
7 changed files with 11383 additions and 19 deletions
+51 -7
View File
@@ -18,6 +18,17 @@ namespace Microsoft.Iris.DecompXml;
partial class Decompiler partial class Decompiler
{ {
private SyntaxTree DecompileScript(uint startOffset, MarkupTypeSchema export) private SyntaxTree DecompileScript(uint startOffset, MarkupTypeSchema export)
{
var statements = DecompileMethod(startOffset, export);
return SyntaxTree(
CompilationUnit().WithMembers(
[..statements.Select(GlobalStatement)]
)
);
}
public List<StatementSyntax> DecompileMethod(uint startOffset, MarkupTypeSchema export)
{ {
var methodBody = _context.GetMethodBody(startOffset).ToArray(); var methodBody = _context.GetMethodBody(startOffset).ToArray();
@@ -164,9 +175,23 @@ partial class Decompiler
blockStack.Push(ifBlock); blockStack.Push(ifBlock);
break; break;
case OpCode.Jump:
var jumpOffset = (uint)instruction.Operands.First().Value;
var statementsJumpedTo = DecompileMethod(jumpOffset, export);
// TODO
blockStack.Peek().Statements.Add(
Block(List([..statementsJumpedTo]))
.WithTrailingTrivia(EndOfLine(Environment.NewLine), Comment($"// TODO: {instruction}"))
);
break;
case not OpCode.ReturnVoid: case not OpCode.ReturnVoid:
if (!TryDecompileExpression(instruction, stack, blockStack)) if (!TryDecompileExpression(instruction, stack, blockStack))
throw new NotImplementedException(); {
var unsupportedComment = Comment($"// Unsupported instruction: {instruction}");
blockStack.Peek().Statements.Add(EmptyStatement().WithLeadingTrivia(unsupportedComment));
}
break; break;
} }
} }
@@ -182,13 +207,22 @@ partial class Decompiler
throw new InvalidOperationException($"Failed to decompile script for {export.Name}, no top-level code blocks"); throw new InvalidOperationException($"Failed to decompile script for {export.Name}, no top-level code blocks");
// Unwrap top-most block to avoid extra curly braces around entire script // Unwrap top-most block to avoid extra curly braces around entire script
var topBlock = blockStack.Pop(); return blockStack.Pop().Statements;
}
return SyntaxTree( private MethodDeclarationSyntax DecompileMethodDeclaration(MarkupMethodSchema method, MarkupTypeSchema export)
CompilationUnit().WithMembers( {
[..topBlock.Statements.Select(GlobalStatement)] var targetType = export.ResolveScriptId(method.CodeOffset, out var offset);
) var methodBody = DecompileMethod(offset, export);
);
var parameters = method.ParameterTypes
.Zip(method.ParameterNames, (t, n) => Parameter(Identifier(n)).WithType(IrisExpression.ToSyntax(t, _context)));
return MethodDeclaration(
IrisExpression.ToSyntax(method.ReturnType, _context),
method.Name)
.WithParameterList(ParameterList([..parameters]))
.WithBody(Block(methodBody));
} }
public static string FormatInlineExpression(ExpressionSyntax expr, CancellationToken token = default) public static string FormatInlineExpression(ExpressionSyntax expr, CancellationToken token = default)
@@ -321,6 +355,16 @@ partial class Decompiler
DecompileOperation(instruction, stack); DecompileOperation(instruction, stack);
break; break;
case OpCode.IsCheck:
var typeToCheckFor = _context.GetImportedType(instruction.Operands.First());
var objToCheck = stack.Pop();
stack.Push(IsPatternExpression(
IrisExpression.ToSyntax(objToCheck, _context),
TypePattern(IrisExpression.ToSyntax(typeToCheckFor, _context))
));
break;
default: default:
return false; return false;
} }
+21 -6
View File
@@ -79,6 +79,26 @@ public partial class Decompiler
} }
} }
if (export.FinalEvaluateOffsets is { Length: > 0 })
{
throw new NotImplementedException();
}
if (export.Methods is { Length: > 0 })
{
var xScripts = GetOrCreateElement(xExport, _nsUix + "Scripts");
foreach (var method in export.Methods.OfType<MarkupMethodSchema>())
{
var methodSyntax = DecompileMethodDeclaration(method, export);
var scriptText = FormatScript(methodSyntax.SyntaxTree);
XElement xScript = new(_nsUix + "Script", scriptText);
xScripts.Add(xScript);
}
}
if (export.RefreshGroupOffsets is { Length: > 0 }) if (export.RefreshGroupOffsets is { Length: > 0 })
{ {
var xScripts = GetOrCreateElement(xExport, _nsUix + "Scripts"); var xScripts = GetOrCreateElement(xExport, _nsUix + "Scripts");
@@ -287,12 +307,7 @@ public partial class Decompiler
if (instruction.OpCode is OpCode.DestructiveListen) if (instruction.OpCode is OpCode.DestructiveListen)
refreshOffset = (uint)instruction.Operands.ElementAt(4).Value; refreshOffset = (uint)instruction.Operands.ElementAt(4).Value;
MarkupTypeSchema markupTypeSchema = initType; var markupTypeSchema = initType.ResolveScriptId(scriptId, out var scriptOffset);
uint num = scriptId >> 27;
while (num != markupTypeSchema.TypeDepth)
markupTypeSchema = markupTypeSchema.MarkupTypeBase;
var scriptOffset = scriptId & 0x07FFFFFFU;
string watch = null; string watch = null;
InstructionObjectSource watchSource = InstructionObjectSource.Dynamic; InstructionObjectSource watchSource = InstructionObjectSource.Dynamic;
+24 -3
View File
@@ -3,13 +3,15 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Iris.Asm; using Microsoft.Iris.Asm;
using Microsoft.Iris.Markup; using Microsoft.Iris.Markup;
using System; using System;
using System.Collections.Generic;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace Microsoft.Iris.DecompXml.Mock; namespace Microsoft.Iris.DecompXml.Mock;
internal static class IrisExpression internal static class IrisExpression
{ {
private static Dictionary<ulong, SyntaxKind> _predefinedTypeMap = null;
public static ExpressionSyntax ToSyntax(object obj, DecompileContext context) public static ExpressionSyntax ToSyntax(object obj, DecompileContext context)
{ {
return obj switch return obj switch
@@ -31,8 +33,13 @@ internal static class IrisExpression
}; };
} }
public static IdentifierNameSyntax ToSyntax(TypeSchema type, DecompileContext context) => public static TypeSyntax ToSyntax(TypeSchema type, DecompileContext context)
IdentifierName(context.GetQualifiedName(type).ToString()); {
if (TryMapPredefinedType(type, out var kind))
return PredefinedType(Token(kind));
return IdentifierName(context.GetQualifiedName(type).ToString());
}
public static IdentifierNameSyntax ToSyntax(SymbolReference symbolRef) => public static IdentifierNameSyntax ToSyntax(SymbolReference symbolRef) =>
IdentifierName(symbolRef.Symbol); IdentifierName(symbolRef.Symbol);
@@ -50,4 +57,18 @@ internal static class IrisExpression
_ => ToSyntax(obj, context), _ => ToSyntax(obj, context),
}; };
} }
private static bool TryMapPredefinedType(TypeSchema type, out SyntaxKind kind)
{
_predefinedTypeMap ??= new()
{
[UIXTypes.MapIDToType(UIXTypeID.Void).UniqueId] = SyntaxKind.VoidKeyword,
[UIXTypes.MapIDToType(UIXTypeID.Int32).UniqueId] = SyntaxKind.IntKeyword,
[UIXTypes.MapIDToType(UIXTypeID.Int64).UniqueId] = SyntaxKind.LongKeyword,
[UIXTypes.MapIDToType(UIXTypeID.String).UniqueId] = SyntaxKind.StringKeyword,
[UIXTypes.MapIDToType(UIXTypeID.Boolean).UniqueId] = SyntaxKind.BoolKeyword,
};
return _predefinedTypeMap.TryGetValue(type.UniqueId, out kind);
}
} }
+6
View File
@@ -15,5 +15,11 @@
<schema:LibraryPlaylistContentQuery Enabled="false" Name="Query" /> <schema:LibraryPlaylistContentQuery Enabled="false" Name="Query" />
<Boolean Boolean="false" Name="ContentNotInLibrary" /> <Boolean Boolean="false" Name="ContentNotInLibrary" />
</Locals> </Locals>
<Scripts>
<Script></Script>
<Script></Script>
<Script></Script>
<Script></Script>
</Scripts>
</Class> </Class>
</UIX> </UIX>
+41 -1
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-16"?> <?xml version="1.0" encoding="utf-16"?>
<UIX xmlns="http://schemas.microsoft.com/2007/uix" xmlns:iris="assembly://UIX/Microsoft.Iris" xmlns:style="res://UIXControls!Style.uix" xmlns:zuneUI="assembly://ZuneShell/ZuneUI" xmlns:imageStyle="res://UIXControls!ImageStyle.uix" xmlns:styles="res://ZuneShellResources!Styles.uix" xmlns:styles1="res://UIXControls!Styles.uix" xmlns:generic="assembly://System.Private.CoreLib/System.Collections.Generic"> <UIX xmlns="http://schemas.microsoft.com/2007/uix" xmlns:iris="assembly://UIX/Microsoft.Iris" xmlns:style="res://UIXControls!Style.uix" xmlns:zuneUI="assembly://ZuneShell/ZuneUI" xmlns:imageStyle="res://UIXControls!ImageStyle.uix" xmlns:styles="res://ZuneShellResources!Styles.uix" xmlns:styles1="res://UIXControls!Styles.uix" xmlns:generic="assembly://System.Private.CoreLib/System.Collections.Generic" xmlns:configuration="assembly://ZuneDBApi/Microsoft.Zune.Configuration">
<Class Name="Styles" Shared="true"> <Class Name="Styles" Shared="true">
<Properties> <Properties>
<zuneUI:FontLoader Resource="ZuneShellResources.dll" Name="FontLoader"> <zuneUI:FontLoader Resource="ZuneShellResources.dll" Name="FontLoader">
@@ -818,6 +818,46 @@
<Image Source="res://ZuneShellResources!WindowsPhoneEnergyBar.png" Name="WindowsPhoneEnergyBar" /> <Image Source="res://ZuneShellResources!WindowsPhoneEnergyBar.png" Name="WindowsPhoneEnergyBar" />
<Image Source="res://ZuneShellResources!WindowsPhoneAppList.png" Name="WindowsPhoneAppList" /> <Image Source="res://ZuneShellResources!WindowsPhoneAppList.png" Name="WindowsPhoneAppList" />
</Properties> </Properties>
<Scripts>
<Script>void UpdateDefaultFrameBackground()
{
DefaultBackgroundImage = new Image(this.GetDefaultFrameBackground());
}</Script>
<Script>string GetDefaultFrameBackground()
{
source = null;
if (!zuneUI:ZuneShell.DefaultInstance.CurrentPage is zuneUI:QuickplayPage)
{
if (zuneUI:ZuneShell.DefaultInstance.CurrentPage is zuneUI:StartupPage)
{
}
if (zuneUI:ZuneShell.DefaultInstance.CurrentPage is zuneUI:StartupPage &amp;&amp; string.IsNullOrEmpty(zuneUI:Shell.SessionStartupPath))
{
}
}
if (!zuneUI:ZuneShell.DefaultInstance.CurrentPage is zuneUI:QuickplayPage || zuneUI:ZuneShell.DefaultInstance.CurrentPage is zuneUI:StartupPage &amp;&amp; string.IsNullOrEmpty(zuneUI:Shell.SessionStartupPath) &amp;&amp; configuration:ClientConfiguration.Shell.StartupPage == zuneUI:Shell.MainFrame.Quickplay.DefaultUIPath)
{
if (zuneUI:ZuneShell.DefaultInstance.CurrentPage is zuneUI:StartupPage)
{
}
}
if (zuneUI:ZuneShell.DefaultInstance.CurrentPage is zuneUI:QuickplayPage || zuneUI:ZuneShell.DefaultInstance.CurrentPage is zuneUI:StartupPage &amp;&amp; string.IsNullOrEmpty(zuneUI:Shell.SessionStartupPath) &amp;&amp; configuration:ClientConfiguration.Shell.StartupPage == zuneUI:Shell.MainFrame.Quickplay.DefaultUIPath || zuneUI:ZuneShell.DefaultInstance.CurrentPage is zuneUI:StartupPage &amp;&amp; zuneUI:Shell.SessionStartupPath == zuneUI:Shell.MainFrame.Quickplay.DefaultUIPath)
{
source = QuickplayFrameBackground.Value;
{
// Unsupported instruction: CSYM 63
;
} // TODO: JMP 170
}// Unsupported instruction: VTC 960
;
source = FrameBackgrounds.get_Item(DefaultBackgroundIndex).Value;
// Unsupported instruction: CSYM 63
;
}</Script>
</Scripts>
</Class> </Class>
<Class Name="SharedStyles" Shared="true"> <Class Name="SharedStyles" Shared="true">
<Properties> <Properties>
File diff suppressed because it is too large Load Diff