mirror of
https://github.com/ZuneDev/ZuneUIXTools.git
synced 2026-07-27 13:11:59 -07:00
Implement If blocks in scripts
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
|
||||
namespace Microsoft.Iris.DecompXml;
|
||||
|
||||
internal class CodeBlockInfo
|
||||
{
|
||||
public CodeBlockInfo(uint startOffset, uint endOffset, SyntaxKind kind, ExpressionSyntax expression = null)
|
||||
{
|
||||
StartOffset = startOffset;
|
||||
EndOffset = endOffset;
|
||||
Kind = kind;
|
||||
Expression = expression;
|
||||
}
|
||||
|
||||
public uint StartOffset { get; init; }
|
||||
|
||||
public uint EndOffset { get; init; }
|
||||
|
||||
public SyntaxKind Kind { get; init; }
|
||||
|
||||
public ExpressionSyntax Expression { get; init; }
|
||||
|
||||
public List<StatementSyntax> Statements { get; } = [];
|
||||
|
||||
public void FinalizeBlock(CodeBlockInfo parentBlock)
|
||||
{
|
||||
var blockBody = Block(Statements);
|
||||
|
||||
switch (Kind)
|
||||
{
|
||||
case SyntaxKind.IfStatement:
|
||||
var ifStatement = IfStatement(Expression, blockBody);
|
||||
parentBlock.Statements.Add(ifStatement);
|
||||
break;
|
||||
|
||||
case SyntaxKind.ElseClause:
|
||||
var elseClause = ElseClause(blockBody);
|
||||
var ifElseBlock = (IfStatementSyntax)parentBlock.Statements[^1];
|
||||
parentBlock.Statements[^1] = ifElseBlock.WithElse(elseClause);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new System.NotImplementedException($"Unrecognized code block kind '{Kind}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,14 +78,14 @@ internal class DecompileContext
|
||||
|
||||
public PropertySchema GetImportedProperty(Operand op) => ImportTables.PropertyImports[(ushort)op.Value];
|
||||
|
||||
public Instruction[] GetMethodBody(uint startOffset)
|
||||
public IEnumerable<Instruction> GetMethodBody(uint startOffset)
|
||||
{
|
||||
return Instructions
|
||||
.SkipWhile(i => i.Offset < startOffset)
|
||||
.OrderBy(i => i.Offset)
|
||||
.TakeWhile(i => i.OpCode is not (OpCode.ReturnValue or OpCode.ReturnVoid))
|
||||
.OrderBy(i => i.Offset)
|
||||
.ToArray();
|
||||
foreach (var instruction in Instructions.SkipWhile(i => i.Offset < startOffset))
|
||||
{
|
||||
yield return instruction;
|
||||
if (instruction.OpCode is OpCode.ReturnValue or OpCode.ReturnVoid)
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<KeyValuePair<string, XNamespace>> GetUsedNamespaces()
|
||||
|
||||
@@ -18,16 +18,36 @@ partial class Decompiler
|
||||
{
|
||||
private SyntaxTree DecompileScript(uint startOffset, MarkupTypeSchema export)
|
||||
{
|
||||
var methodBody = _context.GetMethodBody(startOffset);
|
||||
var methodBody = _context.GetMethodBody(startOffset).ToArray();
|
||||
|
||||
Stack<BlockSyntax> blockStack = [];
|
||||
blockStack.Push(Block());
|
||||
Stack<CodeBlockInfo> blockStack = [];
|
||||
blockStack.Push(new(0, methodBody[^1].Offset, SyntaxKind.Block, null));
|
||||
|
||||
Stack<object> stack = new();
|
||||
|
||||
for (int i = 0; i < methodBody.Length; i++)
|
||||
{
|
||||
var instruction = methodBody[i];
|
||||
|
||||
// TODO: Handle for loops
|
||||
if (instruction.Offset == blockStack.Peek().EndOffset)
|
||||
{
|
||||
// Make sure there is only one top-level block
|
||||
if (blockStack.Count > 1)
|
||||
{
|
||||
var currentBlock = blockStack.Pop();
|
||||
currentBlock.FinalizeBlock(blockStack.Peek());
|
||||
}
|
||||
else
|
||||
{
|
||||
// End of function
|
||||
if (i + 1 != methodBody.Length)
|
||||
throw new InvalidOperationException("Expected end of function!");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var opCode = instruction.OpCode;
|
||||
|
||||
try
|
||||
@@ -73,7 +93,7 @@ partial class Decompiler
|
||||
IrisExpression.ToSyntax(newSymbolValue, _context)
|
||||
);
|
||||
|
||||
AddStatementToBlock(blockStack, ExpressionStatement(symbolAssignmentExpr));
|
||||
blockStack.Peek().Statements.Add(ExpressionStatement(symbolAssignmentExpr));
|
||||
break;
|
||||
|
||||
case OpCode.MethodInvoke:
|
||||
@@ -116,7 +136,7 @@ partial class Decompiler
|
||||
}
|
||||
else
|
||||
{
|
||||
AddStatementToBlock(blockStack, ExpressionStatement(methodResult));
|
||||
blockStack.Peek().Statements.Add(ExpressionStatement(methodResult));
|
||||
}
|
||||
|
||||
if (pushLastParam)
|
||||
@@ -130,7 +150,7 @@ partial class Decompiler
|
||||
case OpCode.PropertyGetStatic:
|
||||
var propToGet = _context.GetImportedProperty(instruction.Operands.First());
|
||||
|
||||
var propTarget = instruction.OpCode switch
|
||||
var propGetTarget = instruction.OpCode switch
|
||||
{
|
||||
OpCode.PropertyGet => stack.Pop(),
|
||||
OpCode.PropertyGetPeek => stack.Peek(),
|
||||
@@ -138,26 +158,36 @@ partial class Decompiler
|
||||
};
|
||||
|
||||
var propertyGetExpression = MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
IrisExpression.ToSyntax(propTarget, _context),
|
||||
IrisExpression.ToSyntax(propGetTarget, _context),
|
||||
IdentifierName(propToGet.Name)
|
||||
);
|
||||
|
||||
stack.Push(propertyGetExpression);
|
||||
break;
|
||||
|
||||
case OpCode.PropertyInitialize:
|
||||
var propertyToInit = _context.GetImportedProperty(instruction.Operands.First());
|
||||
var newPropValue = stack.Pop();
|
||||
case OpCode.PropertyAssign:
|
||||
case OpCode.PropertyAssignStatic:
|
||||
var propToSet = _context.GetImportedProperty(instruction.Operands.First());
|
||||
|
||||
var target = stack.Pop();
|
||||
var xTarget = (XElement)ToXmlFriendlyObject(target);
|
||||
var propSetTarget = opCode is OpCode.PropertyAssignStatic
|
||||
? propToSet.Owner
|
||||
: stack.Pop();
|
||||
|
||||
PropertyAssignOnXElement(xTarget, propertyToInit, IrisObject.Create(newPropValue, propertyToInit.PropertyType, _context));
|
||||
var newPropValue = IrisExpression.ToSyntax(stack.Peek(), _context);
|
||||
|
||||
stack.Push(new IrisObject(xTarget, propertyToInit.Owner));
|
||||
var propertySetExpression = AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
|
||||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
|
||||
IrisExpression.ToSyntax(propSetTarget, _context),
|
||||
IdentifierName(propToSet.Name)
|
||||
),
|
||||
newPropValue
|
||||
);
|
||||
|
||||
blockStack.Peek().Statements.Add(ExpressionStatement(propertySetExpression));
|
||||
break;
|
||||
|
||||
case OpCode.PropertyDictionaryAdd:
|
||||
// TODO
|
||||
var targetDictProperty = _context.GetImportedProperty(instruction.Operands.ElementAt(0));
|
||||
|
||||
var keyReference = instruction.Operands.ElementAt(1);
|
||||
@@ -171,12 +201,74 @@ partial class Decompiler
|
||||
break;
|
||||
|
||||
case OpCode.PropertyListAdd:
|
||||
// TODO
|
||||
var targetListProperty = _context.GetImportedProperty(instruction.Operands.First());
|
||||
var valueToAdd = stack.Pop();
|
||||
var targetInstance2 = (XElement)ToXmlFriendlyObject(stack.Peek());
|
||||
|
||||
PropertyListAddOnXElement(targetInstance2, targetListProperty, IrisObject.Create(valueToAdd, null, _context));
|
||||
break;
|
||||
|
||||
case OpCode.JumpIfFalse:
|
||||
case OpCode.JumpIfFalsePeek:
|
||||
case OpCode.JumpIfTruePeek:
|
||||
var jumpToOffset = (uint)instruction.Operands.First().Value;
|
||||
|
||||
// TODO: What about for loops?
|
||||
if (instruction.Offset > jumpToOffset)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
var isPeek = opCode is OpCode.JumpIfFalsePeek or OpCode.JumpIfTruePeek or OpCode.JumpIfNullPeek;
|
||||
var rawJumpCondition = IrisExpression.ToSyntax(isPeek ? stack.Peek() : stack.Pop(), _context);
|
||||
|
||||
var jumpCondition = opCode switch
|
||||
{
|
||||
OpCode.JumpIfFalse or
|
||||
OpCode.JumpIfFalsePeek => PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, rawJumpCondition),
|
||||
|
||||
OpCode.JumpIfTruePeek => rawJumpCondition,
|
||||
|
||||
OpCode.JumpIfNullPeek => BinaryExpression(SyntaxKind.EqualsExpression,
|
||||
rawJumpCondition,
|
||||
LiteralExpression(SyntaxKind.NullLiteralExpression)),
|
||||
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
var ifBlock = new CodeBlockInfo(instruction.Offset, jumpToOffset, SyntaxKind.IfStatement, jumpCondition);
|
||||
blockStack.Push(ifBlock);
|
||||
break;
|
||||
|
||||
case OpCode.Operation:
|
||||
var opHost = _context.GetImportedType(instruction.Operands.ElementAt(0));
|
||||
|
||||
var op = (OperationType)(int)(byte)instruction.Operands.ElementAt(1).Value;
|
||||
var isUnary = TypeSchema.IsUnaryOperation(op);
|
||||
var opSyntax = OperationToSyntaxKind(op);
|
||||
|
||||
ExpressionSyntax operationExpr;
|
||||
|
||||
if (isUnary)
|
||||
{
|
||||
var left = IrisExpression.ToSyntax(stack.Pop(), _context);
|
||||
var isPostfix = op is OperationType.PostIncrement or OperationType.PostDecrement;
|
||||
|
||||
operationExpr = isPostfix
|
||||
? PostfixUnaryExpression(opSyntax, left)
|
||||
: PrefixUnaryExpression(opSyntax, left);
|
||||
}
|
||||
else
|
||||
{
|
||||
var right = IrisExpression.ToSyntax(stack.Pop(), _context);
|
||||
var left = IrisExpression.ToSyntax(stack.Pop(), _context);
|
||||
|
||||
operationExpr = BinaryExpression(opSyntax, left, right);
|
||||
}
|
||||
|
||||
stack.Push(operationExpr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -216,4 +308,34 @@ partial class Decompiler
|
||||
block = block.AddStatements(statement);
|
||||
blockStack.Push(block);
|
||||
}
|
||||
|
||||
private static SyntaxKind OperationToSyntaxKind(OperationType operation)
|
||||
{
|
||||
return operation switch
|
||||
{
|
||||
OperationType.MathAdd => SyntaxKind.AddExpression,
|
||||
OperationType.MathSubtract => SyntaxKind.SubtractExpression,
|
||||
OperationType.MathMultiply => SyntaxKind.MultiplyExpression,
|
||||
OperationType.MathDivide => SyntaxKind.DivideExpression,
|
||||
OperationType.MathModulus => SyntaxKind.ModuloExpression,
|
||||
OperationType.MathNegate => SyntaxKind.UnaryMinusExpression,
|
||||
|
||||
OperationType.LogicalAnd => SyntaxKind.LogicalAndExpression,
|
||||
OperationType.LogicalOr => SyntaxKind.LogicalOrExpression,
|
||||
OperationType.LogicalNot => SyntaxKind.LogicalNotExpression,
|
||||
|
||||
OperationType.RelationalEquals => SyntaxKind.EqualsExpression,
|
||||
OperationType.RelationalNotEquals => SyntaxKind.NotEqualsExpression,
|
||||
OperationType.RelationalLessThan => SyntaxKind.LessThanExpression,
|
||||
OperationType.RelationalGreaterThan => SyntaxKind.GreaterThanExpression,
|
||||
OperationType.RelationalLessThanEquals => SyntaxKind.LessThanOrEqualExpression,
|
||||
OperationType.RelationalGreaterThanEquals => SyntaxKind.GreaterThanOrEqualExpression,
|
||||
OperationType.RelationalIs => SyntaxKind.IsExpression,
|
||||
|
||||
OperationType.PostIncrement => SyntaxKind.PostIncrementExpression,
|
||||
OperationType.PostDecrement => SyntaxKind.PostDecrementExpression,
|
||||
|
||||
_ => throw new ArgumentException($"Invalid operation type '{operation}'", nameof(operation))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ public partial class Decompiler
|
||||
|
||||
private Stack<object> AnalyzeMethodForInit(uint startOffset, XElement elemToInit, MarkupTypeSchema initType, string methodName = "")
|
||||
{
|
||||
var methodBody = _context.GetMethodBody(startOffset);
|
||||
var methodBody = _context.GetMethodBody(startOffset).ToArray();
|
||||
|
||||
Stack<object> stack = new([elemToInit]);
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.Iris.Asm;
|
||||
using Microsoft.Iris.Markup;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user