Skip to content

Fix a pile of decompiler output bugs + add a cross-platform CLI - #28

Open
Jeremiah-Jahnke wants to merge 7 commits into
ferib:masterfrom
Jeremiah-Jahnke:pr-decompiler-fixes
Open

Fix a pile of decompiler output bugs + add a cross-platform CLI#28
Jeremiah-Jahnke wants to merge 7 commits into
ferib:masterfrom
Jeremiah-Jahnke:pr-decompiler-fixes

Conversation

@Jeremiah-Jahnke

Copy link
Copy Markdown

Been digging through the decompiler and fixed a bunch of bugs that were making it spit out invalid Lua. Each fix is its own commit for readability.

Bugs fixed

  • CALL / TAILCALL arguments - calls whose last arg is ... or another call (e.g. print("hi", ...), f(g())) dropped all their args and came out as foo(). The B == 0 ("args run to the top of the stack") branch looped for (i = A; i < B; ...), which never runs when B is 0. Now it walks back to the VARARG/CALL that set the top and counts from there - print("hi", ...) goes from var1() to var1(var2, var3).
  • VARARG names - local a, b = ... decompiled as local var1var2 = ... (missing comma) and the ... in a call came out with a blank name.
  • local leaking into expressions - local x = a + b came out as local x = local a + local b.
  • Closure names - closures printed IDK_SHIT_WENT_MISSING_BRO because the child function didn't exist yet when the parent's line was built. Now resolved after every function is created, and nested functions get named too.
  • Upvalue names - GETUPVAL/SETUPVAL showed "unk0" and had their operands backwards; they now use the real names from the debug info.

so any call like print("hi", ...) or return fmt(...) got decompiled as var1()
with no args, cause the B==0 branch looped `for(i=A; i<B)` which never runs when
B is 0. B==0 actually means "args go from A+1 up to the top of the stack", so now
we walk back to the VARARG/CALL that set the top and count from there.

also pulled the arg building into a lil WriteArgs() helper so CALL and TAILCALL
share it instead of copy pasting the same broken loop twice.
local a, b = ... was decompiling as "local var1var2 = ..." (no comma) and
print("hi", ...) gave "local  = ..." with an empty name.

the B==0 loop was `for(i=A; i<A+B-1)` which is `i<A-1` when B is 0, so it never
ran, and the comma check used B-2 instead of A+B-2 so the separators never fired
either. rewrote it to just count targets: B>=1 gives B-1 names, B==0 is a variable
amount so we show one var = ... and move on.
stuff like  local x = a + b  came out as  local x = local a + local b  whenever
a/b were declared somewhere that doesnt register them as 'used' (varargs, call
results, etc). WriteConstant was calling WriteIndex without telling it these are
reads, so WriteIndex slapped a 'local' on the first time it saw em.

WriteIndex already takes a useLocalKeyword flag, so just pass false from
WriteConstant since arithmetic operands are always reads.
closures got their name from the child function's ScriptFunction, but the child
didnt exist yet when we built the parent lines (parents get created first, kids
after), so UpdateClosures ran too early and everything came out as
IDK_SHIT_WENT_MISSING_BRO.
also fixed WriteF only naming direct children (grandchildren came out blank) by
just recursing WriteF on the child instead of creating it + looping its kids
separately.
GETUPVAL came out as "unk0" and SETUPVAL had its operands backwards + treated the
value as a constant. turns out luac stashes the upvalue names in the debug info and
the decoder already reads em into DebugUpvalues, we just never used it lol.
- CALL/TAILCALL args, vararg names, local-in-expressions, closure names, upvalue names
- adds cli/ (luadec file.luac) since the demos are all windows-path hardcoded
- .gitignore now ignores bin/obj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant