Skip to content

Commit

Permalink
Fix multiple issues with compound operations
Browse files Browse the repository at this point in the history
  • Loading branch information
colinator27 committed Jul 24, 2024
1 parent 3eb5d6a commit 426bff3
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 4 deletions.
5 changes: 4 additions & 1 deletion Underanalyzer/Decompiler/AST/BlockSimulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,10 @@ private static void SimulateMultiArrayPop(ASTBuilder builder, List<IStatementNod

// Make a copy of the variable we already have, with the new index at the end of array indices
List<IExpressionNode> existingArrayIndices = variable.ArrayIndices ?? throw new DecompilerException("Expected existing array indices");
VariableNode extendedVariable = new(variable.Variable, variable.ReferenceType, variable.Left, new(existingArrayIndices) { index }, variable.RegularPush);
VariableNode extendedVariable = new(variable.Variable, variable.ReferenceType, variable.Left, new(existingArrayIndices) { index }, variable.RegularPush)
{
Duplicated = variable.Duplicated
};

// Make assignment node with this variable, and the value remaining at the top of the stack
IExpressionNode value = builder.ExpressionStack.Pop();
Expand Down
7 changes: 5 additions & 2 deletions Underanalyzer/Decompiler/AST/Nodes/AssignNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,12 @@ public IStatementNode Clean(ASTCleaner cleaner)
Value = Value?.Clean(cleaner);

// Clean up any remaining postfix/compound operations
if (AssignKind == AssignType.Normal && Variable is VariableNode variable && Value is BinaryNode binary)
if (AssignKind == AssignType.Normal && Variable is VariableNode variable && Value is BinaryNode binary &&
binary.Instruction.Kind is Opcode.Add or Opcode.Subtract or Opcode.Multiply or Opcode.Divide or
Opcode.GMLModulo or Opcode.And or Opcode.Or or Opcode.Xor)
{
if (binary.Left is VariableNode binVariable && binVariable.IdenticalToInExpression(variable))
if (binary.Left is VariableNode binVariable && binVariable.IdenticalToInExpression(variable) &&
(variable.Duplicated || variable.IsSimpleVariable()))
{
// This is probably a compound operation

Expand Down
13 changes: 12 additions & 1 deletion Underanalyzer/Decompiler/AST/Nodes/VariableNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -445,9 +445,20 @@ public int GetArgumentIndex()
/// <summary>
/// Returns true if this variable represents the constant <c>undefined</c>, or false otherwise.
/// </summary>
/// <returns></returns>
public bool IsUndefinedVariable()
{
return Variable.Name.Content == "undefined";
}

/// <summary>
/// Returns true if the variable is "simple," meaning that it can be referenced with
/// one instruction, or false otherwise.
/// </summary>
/// <remarks>
/// This occurs in variables with no left-side expression, and no array indices.
/// </remarks>
public bool IsSimpleVariable()
{
return Left is InstanceTypeNode && ArrayIndices is null;
}
}
201 changes: 201 additions & 0 deletions UnderanalyzerTest/DecompileContext.DecompileToString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2393,4 +2393,205 @@ public void TestEmpty()
string decompileResult = decompilerContext.DecompileToString();
Assert.Equal("", decompileResult);
}

[Fact]
public void TestCompoundOperators()
{
TestUtil.VerifyDecompileResult(
"""
push.v self.a
pushi.e 2
add.i.v
pop.v.v self.a
push.v self.a
pushi.e 2
sub.i.v
pop.v.v self.a
push.v self.a
pushi.e 2
mul.i.v
pop.v.v self.a
push.v self.a
pushi.e 2
div.i.v
pop.v.v self.a
push.v self.a
pushi.e 2
and.i.v
pop.v.v self.a
push.v self.a
pushi.e 2
or.i.v
pop.v.v self.a
push.v self.a
pushi.e 2
xor.i.v
pop.v.v self.a
push.v self.a
pushi.e 2
mod.i.v
pop.v.v self.a
push.v self.a
pushi.e 2
rem.i.v
pop.v.v self.a
push.v self.a
pushi.e 2
cmp.i.v EQ
pop.v.b self.a
push.v self.a
pushi.e 2
cmp.i.v NEQ
pop.v.b self.a
push.v self.a
pushi.e 2
cmp.i.v GT
pop.v.b self.a
push.v self.a
pushi.e 2
cmp.i.v LT
pop.v.b self.a
push.v self.a
pushi.e 2
cmp.i.v LTE
pop.v.b self.a
push.v self.a
pushi.e 2
cmp.i.v GTE
pop.v.b self.a
push.v self.a
conv.v.l
pushi.e 2
conv.i.l
shl.l.l
pop.v.l self.a
push.v self.a
conv.v.l
pushi.e 2
conv.i.l
shr.l.l
pop.v.l self.a
""",
"""
a += 2;
a -= 2;
a *= 2;
a /= 2;
a &= 2;
a |= 2;
a ^= 2;
a %= 2;
a = a div 2;
a = a == 2;
a = a != 2;
a = a > 2;
a = a < 2;
a = a <= 2;
a = a >= 2;
a = a << 2;
a = a >> 2;
"""
);
}

[Fact]
public void TestComplexCompoundOperators()
{
TestUtil.VerifyDecompileResult(
"""
push.v self.a
conv.v.i
dup.i 0
push.v [stacktop]self.b
push.e 1
add.i.v
pop.i.v [stacktop]self.b
push.v self.a
conv.v.i
dup.i 0
push.v [stacktop]self.b
pushi.e 1
add.i.v
pop.i.v [stacktop]self.b
push.v self.a
conv.v.i
push.v [stacktop]self.b
pushi.e 1
add.i.v
push.v self.a
conv.v.i
pop.v.v [stacktop]self.b
pushi.e -6
pushi.e 0
push.v [multipushpop]self.a
pushi.e 1
pushac.e
pushi.e 2
dup.i 4
savearef.e
pushaf.e
pushi.e 1
add.i.v
restorearef.e
dup.i 4 5
popaf.e
pushi.e -6
pushi.e 0
push.v [multipush]self.a
pushi.e 1
pushac.e
pushi.e 2
pushaf.e
pushi.e 1
add.i.v
pushi.e -6
pushi.e 0
push.v [multipushpop]self.a
pushi.e 1
pushac.e
pushi.e 2
popaf.e
call.i @@This@@ 0
push.v builtin.a
callv.v 0
pushi.e -9
dup.i 4
push.v [stacktop]self.b
push.e 1
add.i.v
pop.i.v [stacktop]self.b
call.i @@This@@ 0
push.v builtin.a
callv.v 0
pushi.e -9
dup.i 4
push.v [stacktop]self.b
pushi.e 1
add.i.v
pop.i.v [stacktop]self.b
call.i @@This@@ 0
push.v builtin.a
callv.v 0
pushi.e -9
push.v [stacktop]self.b
pushi.e 1
add.i.v
call.i @@This@@ 0
push.v builtin.a
callv.v 0
pushi.e -9
pop.v.v [stacktop]self.b
""",
"""
a.b++;
a.b += 1;
a.b = a.b + 1;
a[0][1][2] += 1;
a[0][1][2] = a[0][1][2] + 1;
a().b++;
a().b += 1;
a().b = a().b + 1;
"""
);
}
}

0 comments on commit 426bff3

Please sign in to comment.