Skip to content

Commit

Permalink
Experiments around a decompiler
Browse files Browse the repository at this point in the history
  • Loading branch information
Gui-Yom committed May 29, 2022
1 parent d0bf3ee commit 1fec720
Show file tree
Hide file tree
Showing 3 changed files with 505 additions and 307 deletions.
24 changes: 9 additions & 15 deletions hlbc-cli/src/parser.rs → hlbc-cli/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub enum Command {
SaveTo(String),
Callgraph(usize, usize),
RefTo(ElementRef),
DumpType(usize),
}

#[derive(Debug, Default)]
Expand Down Expand Up @@ -79,15 +80,9 @@ pub fn command_parser(ctx: &ParseContext) -> impl Parser<char, Command, Error =
cmd!("help").map(|_| Help),
cmd!("info").map(|_| Info),
cmd!("entrypoint").map(|_| Entrypoint),
cmd!("int", "i"; index_range(ctx.int_max))
.map(Int)
.labelled("int command"),
cmd!("float", "f"; index_range(ctx.float_max))
.map(Float)
.labelled("float command"),
cmd!("string", "s"; index_range(ctx.string_max))
.map(String)
.labelled("string command"),
cmd!("int", "i"; index_range(ctx.int_max)).map(Int),
cmd!("float", "f"; index_range(ctx.float_max)).map(Float),
cmd!("string", "s"; index_range(ctx.string_max)).map(String),
cmd!("sstr"; any().repeated()).map(|v| SearchStr(v.into_iter().collect())),
cmd!("debugfile", "file"; index_range(ctx.debug_file_max)).map(Debugfile),
cmd!("sfile"; any().repeated()).map(|v| SearchDebugfile(v.into_iter().collect())),
Expand All @@ -109,18 +104,17 @@ pub fn command_parser(ctx: &ParseContext) -> impl Parser<char, Command, Error =
cmd!("callgraph")
.ignore_then(num())
.then(num())
.map(|(f, d)| Callgraph(f, d))
.labelled("callgraph command"),
.map(|(f, d)| Callgraph(f, d)),
cmd!("refto")
.ignore_then(choice((
just("string@").ignore_then(num()).map(ElementRef::String),
just("global@").ignore_then(num()).map(ElementRef::Global),
just("fn@").ignore_then(num()).map(ElementRef::Fn),
)))
.map(RefTo)
.labelled("refto command"),
.map(RefTo),
cmd!("dumptype"; num()).map(DumpType),
))
.labelled("Command")
.labelled("command")
}

fn num() -> impl Parser<char, usize, Error = Simple<char>> {
Expand Down Expand Up @@ -155,7 +149,7 @@ fn index_range(max: usize) -> impl Parser<char, IndexIter, Error = Simple<char>>
mod tests {
use chumsky::Parser;

use crate::parser::{index_range, parse_command, Command, FileOrIndex, ParseContext};
use crate::command::{index_range, parse_command, Command, FileOrIndex, ParseContext};

#[test]
fn test_index_range() {
Expand Down
172 changes: 172 additions & 0 deletions hlbc-cli/src/decompiler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
use std::fmt::Write;

use hlbc::opcodes::Opcode;
use hlbc::types::{Function, RefField, Type, TypeObj};
use hlbc::Bytecode;

pub fn decompile_class(code: &Bytecode, obj: &TypeObj) -> String {
let mut buf = String::with_capacity(1024);

let mut is_static = false;
writeln!(
&mut buf,
"class {} {}{{",
obj.name.display(&code),
if let Some(e) = obj.super_ {
is_static = e.0 == 12;
format!("extends {} ", e.display(&code))
} else {
"".to_string()
}
)
.unwrap();
let indent = " ";
for (i, f) in obj
.fields
.iter()
.enumerate()
.skip(obj.fields.len() - obj.own_fields.len())
{
if obj.bindings.get(&RefField(i)).is_some() {
continue;
}
writeln!(
&mut buf,
"{indent}{}var {}: {}",
if is_static { "static " } else { "" },
f.name.display(&code),
f.t.display(&code)
)
.unwrap();
}
writeln!(&mut buf, "// BINDINGS").unwrap();

for (fi, fun) in &obj.bindings {
//let fi = &obj.fields[fi.0];
let fun = fun.resolve_as_fn(code).unwrap();
writeln!(
&mut buf,
"{indent}{}{}{}{indent}}}\n",
if is_static { "static " } else { "" },
decompile_function_header(code, fun),
decompile_function_body(code, &format!("{indent} "), fun)
)
.unwrap();
}

writeln!(&mut buf, "// METHODS").unwrap();

for f in &obj.protos {
let f = f.findex.resolve_as_fn(code).unwrap();
writeln!(
&mut buf,
"{indent}{}{}{indent}}}\n",
decompile_function_header(code, f),
decompile_function_body(code, &format!("{indent} "), f)
)
.unwrap();
}
writeln!(&mut buf, "}}").unwrap();
buf
}

pub fn decompile_function_header(code: &Bytecode, f: &Function) -> String {
let mut buf = String::with_capacity(256);

write!(&mut buf, "function {}(", f.name.unwrap().display(code)).unwrap();

match f.t.resolve(&code.types) {
Type::Fun(fun) => {
// Skip the first because its a method (this)
for (i, a) in fun.args.iter().enumerate().skip(1) {
if i != 1 {
write!(&mut buf, ", ").unwrap();
}
write!(&mut buf, "reg{}: {}", i, a.display(code)).unwrap();
}
writeln!(
&mut buf,
"): {} {{ // {}",
fun.ret.display(code),
f.findex.0
)
.unwrap();
}
_ => {
unreachable!()
}
}
buf
}

pub fn decompile_closure(code: &Bytecode, indent: &str, f: &Function) -> String {
let mut buf = String::with_capacity(256);

write!(&mut buf, "(").unwrap();

match f.t.resolve(&code.types) {
Type::Fun(fun) => {
// Skip the first because its a method (this)
for (i, a) in fun.args.iter().enumerate().skip(1) {
if i != 1 {
write!(&mut buf, ", ").unwrap();
}
write!(&mut buf, "reg{}: {}", i, a.display(code)).unwrap();
}
writeln!(&mut buf, ") -> {{ // {}", f.findex.0).unwrap();
}
_ => {
unreachable!()
}
}
write!(
&mut buf,
"{}",
decompile_function_body(code, &format!("{indent} "), f)
)
.unwrap();

writeln!(&mut buf, "{indent}}}").unwrap();
buf
}

pub fn decompile_function_body(code: &Bytecode, indent: &str, f: &Function) -> String {
let mut buf = String::with_capacity(256);

for (i, r) in f
.regs
.iter()
.enumerate()
.skip(f.t.resolve(&code.types).get_type_fun().unwrap().args.len())
{
// Skip void type
if !r.is_void() {
writeln!(&mut buf, "{indent}var reg{i}: {}", r.display(code)).unwrap();
}
}

for (i, o) in f.ops.iter().enumerate() {
write!(&mut buf, "{indent}").unwrap();
match o {
Opcode::InstanceClosure { dst, obj, fun } => {
write!(
&mut buf,
"{dst} = {}",
decompile_closure(code, &indent, fun.resolve_as_fn(code).unwrap())
)
.unwrap();
}
Opcode::Ret { ret } => {
// Skip void type
if i != f.ops.len() - 1 || !f.regtype(*ret).is_void() {
writeln!(&mut buf, "return {ret}").unwrap();
}
}
_ => {
writeln!(&mut buf, "{}", o.display(code, f, 0)).unwrap();
}
}
}

buf
}
Loading

0 comments on commit 1fec720

Please sign in to comment.