diff --git a/libs/UIX.DecompXml/Decompiler.Script.cs b/libs/UIX.DecompXml/Decompiler.Script.cs index 0513f4e..145cb26 100644 --- a/libs/UIX.DecompXml/Decompiler.Script.cs +++ b/libs/UIX.DecompXml/Decompiler.Script.cs @@ -37,6 +37,7 @@ partial class Decompiler Stack blockStack = []; blockStack.Push(new(0, methodBody[^1].Offset, SyntaxKind.Block, null)); + HashSet scopedLocals = []; Stack stack = new(); for (int i = 0; i < methodBody.Length; i++) @@ -98,12 +99,36 @@ partial class Decompiler var newSymbolValue = opCode is OpCode.WriteSymbolPeek ? stack.Peek() : stack.Pop(); - var symbolAssignmentExpr = AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, - IrisExpression.ToSyntax(export.SymbolReferenceTable[writeSymbolIndex]), - IrisExpression.ToSyntax(newSymbolValue, _context) - ); + var symbolRef = export.SymbolReferenceTable[writeSymbolIndex]; + var symbolIdentifierExpr = IrisExpression.ToSyntax(symbolRef); + var newSymbolValueExpr = IrisExpression.ToSyntax(newSymbolValue, _context); - blockStack.Peek().Statements.Add(ExpressionStatement(symbolAssignmentExpr)); + StatementSyntax symbolWriteExpr; + + if (symbolRef.Origin is SymbolOrigin.ScopedLocal && !scopedLocals.Contains(symbolRef.Symbol)) + { + // Scoped locals need to be declared the first time they're assigned + var newSymbolIrisObj = IrisObject.Create(newSymbolValue, null, _context); + var typeSchema = newSymbolIrisObj.Type ?? UIXTypes.MapIDToType(UIXTypeID.Object); + + symbolWriteExpr = LocalDeclarationStatement(VariableDeclaration( + IrisExpression.ToSyntax(typeSchema, _context), + SingletonSeparatedList( + VariableDeclarator(symbolRef.Symbol) + .WithInitializer(EqualsValueClause(newSymbolValueExpr)) + ) + )); + } + else + { + var symbolAssignmentExpr = AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, + symbolIdentifierExpr, + newSymbolValueExpr + ); + symbolWriteExpr = ExpressionStatement(symbolAssignmentExpr); + } + + blockStack.Peek().Statements.Add(symbolWriteExpr); break; case OpCode.PropertyAssign: diff --git a/libs/UIX.DecompXml/Mock/IrisObject.cs b/libs/UIX.DecompXml/Mock/IrisObject.cs index a938778..8d4e21a 100644 --- a/libs/UIX.DecompXml/Mock/IrisObject.cs +++ b/libs/UIX.DecompXml/Mock/IrisObject.cs @@ -25,6 +25,7 @@ internal record IrisObject(object Object, TypeSchema Type) else if (objIn is ExpressionSyntax expr) { type ??= GuessExpressionReturnType(expr, context); + return new(obj, type); } if (objIn is Disassembler.RawConstantInfo constantInfo) @@ -40,6 +41,9 @@ internal record IrisObject(object Object, TypeSchema Type) private static TypeSchema GuessExpressionReturnType(ExpressionSyntax expr, DecompileContext ctx) { + if (expr is ParenthesizedExpressionSyntax parenExpr) + expr = parenExpr.Expression; + if (expr is MemberAccessExpressionSyntax memberAccessExpression) { // TODO: Handle member access on instance methods