Skip to content

Commit

Permalink
Merge pull request #1191 from Jacky720/fix2_3returns
Browse files Browse the repository at this point in the history
  • Loading branch information
Grossley authored Mar 2, 2023
2 parents 417dc35 + 60ea473 commit 277c688
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 26 deletions.
76 changes: 66 additions & 10 deletions UndertaleModLib/Compiler/AssemblyWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UndertaleModLib.Decompiler;
using UndertaleModLib.Models;
using static UndertaleModLib.Models.UndertaleInstruction;

Expand Down Expand Up @@ -246,8 +247,46 @@ bool hasLocal(string name)
Decompiler.Decompiler.BuildSubFunctionCache(compileContext.Data);
foreach (var patch in funcPatches)
{
if (patch.isNewFunc)
{
UndertaleString childName = new("gml_Script_" + patch.Name);
int childNameIndex = compileContext.Data.Strings.Count;
compileContext.Data.Strings.Add(childName);

UndertaleCode childEntry = new()
{
Name = childName,
Length = compileContext.OriginalCode.Length, // todo: get a more certainly up-to-date length
ParentEntry = compileContext.OriginalCode,
Offset = patch.Offset,
ArgumentsCount = (ushort)patch.ArgCount,
LocalsCount = compileContext.OriginalCode.LocalsCount // todo: use just the locals for the individual script
};
compileContext.OriginalCode.ChildEntries.Add(childEntry);
int childEntryIndex = compileContext.Data.Code.IndexOf(compileContext.OriginalCode) + compileContext.OriginalCode.ChildEntries.Count;
compileContext.Data.Code.Insert(childEntryIndex, childEntry);

UndertaleScript childScript = new()
{
Name = childName,
Code = childEntry
};
compileContext.Data.Scripts.Add(childScript);

UndertaleFunction childFunction = new()
{
Name = childName,
NameStringID = childNameIndex,
Autogenerated = true
};
compileContext.Data.Functions.Add(childFunction);

compileContext.Data.KnownSubFunctions.Add(patch.Name, childFunction);

continue;
}

UndertaleFunction def;
// Exceptions do occur, but for the moment only in 2.3.
if (patch.ArgCount >= 0)
{
patch.Target.ArgumentsCount = (ushort)patch.ArgCount;
Expand All @@ -274,7 +313,7 @@ bool hasLocal(string name)
else
{
def = compileContext.Data.Functions.ByName(patch.Name);
// This is locked by the 2.3 block above, but this code is only reachable using a 2.3 function definition.
// This code is only reachable using a 2.3 function definition. ("push.i gml_Script_scr_stuff")
def ??= compileContext.Data.KnownSubFunctions.GetValueOrDefault(patch.Name);
if (compileContext.ensureFunctionsDefined)
def ??= compileContext.Data.Functions.EnsureDefined(patch.Name, compileContext.Data.Strings, true);
Expand Down Expand Up @@ -342,6 +381,8 @@ public class FunctionPatch
public UndertaleInstruction Target;
public string Name;
public int ArgCount;
public uint Offset;
public bool isNewFunc = false;
}

public class StringPatch
Expand Down Expand Up @@ -1016,12 +1057,7 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
else
{
// Returns nothing, basically the same as exit
// TODO: I'm pretty sure the "remaining" part is actually just because the decompiler keeps adding
// returns and it's necessary to preserve 1:1 compilation
// But this workaround causes issue https://github.com/krzys-h/UndertaleModTool/issues/900
// So it would be fixed by cutting the "remaining" check here and removing the extra from decompilation.
if (!(CompileContext.GMS2_3 && remaining == 1))
AssembleExit(cw);
AssembleExit(cw);
}
break;
case Parser.Statement.StatementKind.Exit:
Expand Down Expand Up @@ -1237,6 +1273,27 @@ private static void AssembleExpression(CodeWriter cw, Parser.Statement e, Parser
Patch startPatch = Patch.StartHere(cw);
Patch endPatch = Patch.Start();
endPatch.Add(cw.Emit(Opcode.B));
// we're accessing a subfunction here, so build the cache if needed
Decompiler.Decompiler.BuildSubFunctionCache(cw.compileContext.Data);
if (cw.compileContext.Data.KnownSubFunctions.ContainsKey(funcDefName.Text))
{
string subFunctionName = cw.compileContext.Data.KnownSubFunctions[funcDefName.Text].Name.Content;
UndertaleCode childEntry = cw.compileContext.OriginalCode.ChildEntries.ByName(subFunctionName);
childEntry.Offset = cw.offset * 4;
childEntry.ArgumentsCount = (ushort)e.Children[0].Children.Count;
childEntry.LocalsCount = cw.compileContext.OriginalCode.LocalsCount; // todo: use just the locals for the individual script
}
else // we're making a new function baby
{
cw.funcPatches.Add(new FunctionPatch()
{
Name = funcDefName.Text,
Offset = cw.offset * 4,
ArgCount = (ushort)e.Children[0].Children.Count,
isNewFunc = true
});
}

cw.loopContexts.Push(new LoopContext(endPatch, startPatch));
AssembleStatement(cw, e.Children[1]); // body
AssembleExit(cw);
Expand All @@ -1249,7 +1306,6 @@ private static void AssembleExpression(CodeWriter cw, Parser.Statement e, Parser
Name = funcDefName.Text,
ArgCount = -1
});
//cw.compileContext.Data.Code.ByName("gml_GlobalScript_" + funcDefName.Text).ArgumentsCount = (ushort)e.Children[0].Children.Count; // Figure this out in non-convoluted and working way
cw.Emit(Opcode.Conv, DataType.Int32, DataType.Variable);
cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)-1;
cw.Emit(Opcode.Conv, DataType.Int32, DataType.Variable);
Expand All @@ -1261,7 +1317,7 @@ private static void AssembleExpression(CodeWriter cw, Parser.Statement e, Parser
});
cw.typeStack.Push(DataType.Variable);
cw.Emit(Opcode.Dup, DataType.Variable).Extra = 0;
cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)-1;
cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)-1; // todo: -6 sometimes?
}
break;
case Parser.Statement.StatementKind.ExprBinaryOp:
Expand Down
6 changes: 3 additions & 3 deletions UndertaleModLib/Compiler/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -660,12 +660,12 @@ private static Statement ParseFunction(CompileContext context)
{
Statement result = new Statement(Statement.StatementKind.FunctionDef, EnsureTokenKind(TokenKind.KeywordFunction).Token);
Statement args = new Statement();
bool expression = true;
bool expressionMode = true;
Statement destination = null;

if (GetNextTokenKind() == TokenKind.ProcFunction)
{
expression = false;
expressionMode = false;
Statement s = remainingStageOne.Dequeue();
destination = new Statement(Statement.StatementKind.ExprFuncName, s.Token) { ID = s.ID };
}
Expand All @@ -691,7 +691,7 @@ private static Statement ParseFunction(CompileContext context)
if (EnsureTokenKind(TokenKind.CloseParen) == null) return null;

result.Children.Add(ParseStatement(context));
if (expression)
if (expressionMode)
return result;
else // Whatever you call non-anonymous definitions
{
Expand Down
49 changes: 36 additions & 13 deletions UndertaleModLib/Decompiler/Decompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2065,17 +2065,38 @@ internal static void DecompileFromBlock(DecompileContext context, Dictionary<uin

case UndertaleInstruction.Opcode.Ret:
case UndertaleInstruction.Opcode.Exit:
ReturnStatement stmt = new ReturnStatement(instr.Kind == UndertaleInstruction.Opcode.Ret ? stack.Pop() : null);
/*
This shouldn't be necessary: all unused things on the stack get converted to tempvars at the end anyway, and this fixes decompilation of repeat()
See #85
foreach (var expr in stack.Reverse())
if (!(expr is ExpressionTempVar))
statements.Add(expr);
stack.Clear();
*/
statements.Add(stmt);
// 2.3 scripts add exits to every script, even those that lack a return
// This detects that type of exit using the next block.
Block nextBlock = null;
if (DecompileContext.GMS2_3 && instr.Kind == UndertaleInstruction.Opcode.Exit)
{
uint[] blockAddresses = blocks.Keys.ToArray();
Array.Sort(blockAddresses);
int nextBlockIndex = Array.IndexOf(blockAddresses, block.Address ?? 0) + 1;
if (blockAddresses.Length > nextBlockIndex)
{
uint nextBlockAddress = blockAddresses[nextBlockIndex];
nextBlock = blocks[nextBlockAddress];
}
}

if (!(nextBlock is not null
&& nextBlock.Instructions.Count > 0
&& nextBlock.Instructions[0].Kind == UndertaleInstruction.Opcode.Push
&& nextBlock.Instructions[0].Value.GetType() != typeof(int)))
{
ReturnStatement stmt = new ReturnStatement(instr.Kind == UndertaleInstruction.Opcode.Ret ? stack.Pop() : null);
/*
This shouldn't be necessary: all unused things on the stack get converted to tempvars at the end anyway, and this fixes decompilation of repeat()
See #85
foreach (var expr in stack.Reverse())
if (!(expr is ExpressionTempVar))
statements.Add(expr);
stack.Clear();
*/
statements.Add(stmt);
}
end = true;
returned = true;
break;
Expand Down Expand Up @@ -2384,7 +2405,7 @@ internal static void DecompileFromBlock(DecompileContext context, Dictionary<uin
if (callTargetBody != null && callTargetBody.ParentEntry != null && !context.DisableAnonymousFunctionNameResolution)
{
// Special case: this is a direct reference to a method variable
// Figure out what it's actual name is
// Figure out what its actual name is

static string FindActualNameForAnonymousCodeObject(DecompileContext context, UndertaleCode anonymousCodeObject)
{
Expand All @@ -2396,14 +2417,16 @@ static string FindActualNameForAnonymousCodeObject(DecompileContext context, Und
Dictionary<uint, Block> blocks2 = PrepareDecompileFlow(anonymousCodeObject.ParentEntry, new List<uint>() { 0 });
DecompileFromBlock(childContext, blocks2, blocks2[0]);
// This hack handles decompilation of code entries getting shorter, but not longer or out of place.
// Probably is no longer needed since we now update Length mostly-correctly
Block lastBlock;
if (!blocks2.TryGetValue(anonymousCodeObject.Length / 4, out lastBlock))
lastBlock = blocks2[blocks2.Keys.Max()];
List<Statement> statements = HLDecompile(childContext, blocks2, blocks2[0], lastBlock);
foreach (Statement stmt2 in statements)
{
if (stmt2 is AssignmentStatement assign &&
assign.Value is FunctionDefinition funcDef)
assign.Value is FunctionDefinition funcDef &&
funcDef.FunctionBodyCodeEntry == anonymousCodeObject)
{
if (funcDef.FunctionBodyEntryBlock.Address == anonymousCodeObject.Offset / 4)
return assign.Destination.Var.Name.Content;
Expand Down

0 comments on commit 277c688

Please sign in to comment.