Skip to content

Commit

Permalink
Fix #8
Browse files Browse the repository at this point in the history
  • Loading branch information
colinator27 committed Aug 12, 2024
1 parent 409138d commit ffe8312
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 16 deletions.
4 changes: 2 additions & 2 deletions Underanalyzer/Decompiler/AST/Nodes/FunctionDeclNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ private void CleanDefaultArgumentValues(ASTCleaner cleaner)

// Verify the left variable is an argument we have not yet provided a default value for,
// and is strictly greater than the previous argument index
int argIndex = argumentVariable.GetArgumentIndex();
int argIndex = argumentVariable.GetArgumentIndex(cleaner.TopFragmentContext!.MaxReferencedArgument);
if (argIndex == -1 || ArgumentDefaultValues.ContainsKey(argIndex) || argIndex <= lastArgumentIndex)
{
break;
Expand All @@ -129,7 +129,7 @@ private void CleanDefaultArgumentValues(ASTCleaner cleaner)
}

// Assignment's destination should be the same argument variable
if (assign.Variable is not VariableNode assignDest || assignDest.GetArgumentIndex() != argIndex)
if (assign.Variable is not VariableNode assignDest || assignDest.GetArgumentIndex(cleaner.TopFragmentContext!.MaxReferencedArgument) != argIndex)
{
break;
}
Expand Down
59 changes: 45 additions & 14 deletions Underanalyzer/Decompiler/AST/Nodes/VariableNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This Source Code Form is subject to the terms of the Mozilla Public
file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

using System;
using System.Collections.Generic;
using Underanalyzer.Decompiler.GameSpecific;
using static Underanalyzer.IGMInstruction;
Expand Down Expand Up @@ -252,7 +253,7 @@ public IExpressionNode Clean(ASTCleaner cleaner)
// Check if we're a regular argument, and set maximum referenced argument variable if so
if (instType == (int)InstanceType.Argument)
{
int num = GetArgumentIndex();
int num = GetArgumentIndex(cleaner.TopFragmentContext!.MaxReferencedArgument);
if (num != -1)
{
if (num > cleaner.TopFragmentContext!.MaxReferencedArgument)
Expand Down Expand Up @@ -341,7 +342,8 @@ public void Print(ASTPrinter printer)
printer.Write('.');
}

int argIndex = GetArgumentIndex();
int argIndex = GetArgumentIndex(printer.TopFragmentContext!.MaxReferencedArgument);
bool namedArgumentArray = false;
if (argIndex == -1)
{
// Variable name
Expand All @@ -354,6 +356,9 @@ public void Print(ASTPrinter printer)
if (namedArg is not null)
{
printer.Write(namedArg);

// If the variable is a case like "argument[16]", track this so we omit the array index later
namedArgumentArray = Variable.Name.Content == "argument";
}
else
{
Expand All @@ -366,17 +371,32 @@ public void Print(ASTPrinter printer)
// Print array indices
if (printer.Context.GMLv2)
{
// For GMLv2
foreach (IExpressionNode index in ArrayIndices)
// For GMLv2, an arbitrary number of array indices are supported
if (namedArgumentArray)
{
printer.Write('[');
index.Print(printer);
printer.Write(']');
// Named argument array access; skip first index
for (int i = 1; i < ArrayIndices.Count; i++)
{
IExpressionNode index = ArrayIndices[i];
printer.Write('[');
index.Print(printer);
printer.Write(']');
}
}
else
{
// Normal variable; print all of its indices
foreach (IExpressionNode index in ArrayIndices)
{
printer.Write('[');
index.Print(printer);
printer.Write(']');
}
}
}
else
{
// For GMLv1
// For GMLv1, only two array indices are supported
printer.Write('[');
ArrayIndices[0].Print(printer);
if (ArrayIndices.Count == 2)
Expand Down Expand Up @@ -425,17 +445,28 @@ public bool RequiresMultipleLines(ASTPrinter printer)
/// <summary>
/// Returns the argument index this variable represents, or -1 if this is not an argument variable.
/// </summary>
public int GetArgumentIndex()
/// <remarks>
/// Meant for named arguments, so this returns -1 for cases such as argument[0].
/// </remarks>
public int GetArgumentIndex(int maxArgumentArrayIndex)
{
string variableName = Variable.Name.Content;

if (variableName.StartsWith("argument") &&
variableName.Length >= "argument".Length + 1 &&
variableName.Length <= "argument".Length + 2)
if (variableName.StartsWith("argument", StringComparison.InvariantCulture))
{
if (int.TryParse(variableName["argument".Length..], out int num) && num >= 0 && num <= 15)
if (variableName.Length >= "argument".Length + 1 &&
variableName.Length <= "argument".Length + 2)
{
// Normal argument variable
if (int.TryParse(variableName["argument".Length..], out int num) && num >= 0 && num <= 15)
{
return num;
}
}
else if (variableName == "argument" && ArrayIndices is [Int16Node { Value: >= 16 } index, ..] && index.Value <= maxArgumentArrayIndex)
{
return num;
// Argument, using array access (introduced in 2024.8)
return index.Value;
}
}

Expand Down
127 changes: 127 additions & 0 deletions UnderanalyzerTest/DecompileContext.DecompileToString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2594,4 +2594,131 @@ pop.v.v [stacktop]self.b
"""
);
}

[Fact]
public void TestOver16NamedArguments()
{
TestUtil.VerifyDecompileResult(
"""
:[0]
b [2]

> gml_Script_First (locals=0, args=17)
:[1]
push.v arg.argument15
pop.v.v builtin.a
pushi.e -15
pushi.e 16
push.v [array]self.argument
pop.v.v builtin.b
exit.i

:[2]
push.i [function]gml_Script_First
conv.i.v
pushi.e -1
conv.i.v
call.i method 2
dup.v 0
pushi.e -1
pop.v.v [stacktop]self.First
popz.v
b [4]

> gml_Script_Second (locals=0, args=18)
:[3]
push.v arg.argument15
pop.v.v builtin.a
pushi.e -15
pushi.e 16
push.v [array]self.argument
pop.v.v builtin.b
exit.i

:[4]
push.i [function]gml_Script_Second
conv.i.v
pushi.e -1
conv.i.v
call.i method 2
dup.v 0
pushi.e -1
pop.v.v [stacktop]self.Second
popz.v
b [6]

> gml_Script_Third (locals=0, args=16)
:[5]
push.v arg.argument15
pop.v.v builtin.a
pushi.e -15
pushi.e 16
push.v [array]self.argument
pop.v.v builtin.b
exit.i

:[6]
push.i [function]gml_Script_Third
conv.i.v
pushi.e -1
conv.i.v
call.i method 2
dup.v 0
pushi.e -1
pop.v.v [stacktop]self.Third
popz.v
b [8]

> gml_Script_Fourth (locals=0, args=17)
:[7]
pushi.e -15
pushi.e 16
push.v [multipush]self.argument
pushi.e 123
pushaf.e
pop.v.v builtin.a
exit.i

:[8]
push.i [function]gml_Script_Fourth
conv.i.v
pushi.e -1
conv.i.v
call.i method 2
dup.v 0
pushi.e -1
pop.v.v [stacktop]self.Fourth
popz.v
""",
"""
function First(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16)
{
a = _15;
b = _16;
}

function Second(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17)
{
a = _15;
b = _16;
}

function Third(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15)
{
a = _15;
b = argument[16];
}

function Fourth(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16)
{
a = _16[123];
}
""",
null,
new DecompileSettings()
{
UnknownArgumentNamePattern = "_{0}"
}
);
}
}

0 comments on commit ffe8312

Please sign in to comment.