changelog
Every release of the decompiler, newest first. Each is named by its version, the one the API sends with every result, so you can look up what changed in the version that decompiled your file.
activity
the site and the api are live
- Decompile a file in your browser: drop it on unreadable.gg. The source shows beside your file's bytes, with its verdict, ready to copy or download.
- Scripts and agents can send the same file to
POST https://api.unreadable.gg/v1/decompileand get back the source, a verdict, notes and the version that wrote it. See the API docs. - Every connection gets 5 free decompiles a day, on the site and through the API together, with no key or account. Errors and refused decompiles don't count.
- Your files are never stored or logged: see privacy.
bytecode version 14
- Reads bytecode version 14 and the calls current Luau compilers write by default. Modules from them used to be refused.
- Notes and errors carry fixed codes a program can act on, such as
function-refusedandnot-luau-bytecode: see notes and errors. - A function the compiler kept from code it removed, such as the body of
if false then … end, is printed back insideif false then … endinstead of being left out. Where it has to guess what the function captured, the guess is marked. - Fixed a case where the first statements of a loop could be left out of the output.
- The parameters of a function that also takes
...keep their names, and so do locals that are copied and read later. local a, b = -0.5, #xprints as one list, as it was written.- Very large modules decompile much faster at
-O0and-O1. - Three more shapes are refused rather than guessed: a branch whose inner arm computes a value, a table constructor whose parts don't agree, and a function that needs too many passes to settle.
continue in nested loops
- A
continueinside nested loops comes back ascontinue. - An assignment to several places at once, such as
a.x, b.y = 1, 2, comes back as one statement.
fewer wrong programs
- Fixed several cases where the output could have run differently from the input: assignments to undeclared and captured variables, and generic
forloops left by areturn. Where one can't be printed faithfully yet, it's refused instead. - A module that had started taking seconds to decompile is back to a few hundred milliseconds.
- The decompiler builds for WebAssembly, which is how the site and the API run it.
conditions as one expression
- Conditions with three or more tests print as one
and/orexpression. - A variable a loop carries from one pass to the next is no longer declared again on every pass.
and/or as values
and/orchains used as values fold back into one expression, as inlocal x = a and b or c.- A damaged or crafted file can no longer make the decompiler reserve memory for more than the file holds: every count it reads is checked first.
table constructors and recorded types
- Table constructors come back as
{ … }instead of a table filled in one assignment at a time. - A type the bytecode records for a local is printed with it, as in
local w: number. if notkeeps its two arms in the order they were written, and ado … endblock keeps its locals inside it.- A statement that starts with
(gets a;before it, so it isn't read as a call on the line above. - A file from another Lua version is refused by name instead of read as Luau. See Lua versions.
first version
- Decompiles Luau bytecode back into source, and marks or refuses what it can't reproduce instead of guessing.
- Reading a file can't crash the decompiler: a crafted file that would overflow its stack is turned away, and nothing on the way in may abort.