Skip to content

Commit

Permalink
refactor: Update README and code examples for clarity; resolve global…
Browse files Browse the repository at this point in the history
…s in IRTrace
  • Loading branch information
N3rdL0rd committed Dec 24, 2024
1 parent 62313b8 commit 723cbb2
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 12 deletions.
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ Either:
```py
from crashlink import *
code = Bytecode.from_path("path/to/file.hl")
for func in code.functions:
if func.findex.value == 22 or func.findex.value == 240: # typical entry points that the compiler generates
print(disasm.func(code, func))
if code.fn(22): # 22 and 240 are typical entry points for the compiler to generate
print(disasm.func(code.fn(22)))
elif code.fn(240):
print(disasm.func(code.fn(240)))
# > f@22 static $Clazz.main () -> Void (from Clazz.hx)
# > Reg types:
# > 0. Void
Expand Down Expand Up @@ -107,6 +108,27 @@ You can use the following pre-defined commands with `just`:
> [!NOTE]
> IR and the IR optimization layers have not yet been fully implemented.
## Roadmap

- [ ] IR lifter (layer 0)
- [x] If statements
- [ ] Loops
- [ ] Switch opcode statements
- [ ] Function calls
- [ ] CallClosure
- [ ] Closures
- [ ] IR optimization layers
- [ ] SSA locals
- [ ] Trace optimization
- [ ] Nested if/else/if/else -> switch
- [ ] IR -> Haxe
- [ ] Partial recompilation (against stubs of other functions)
- [ ] GUI? (customtkinter or dearpygui)
- [ ] Graphical disassembler
- [ ] Embedded CFG viewer through some Graphviz bindings
- [ ] Decompiler and patching interface
- [ ] IR layer viewer

## Credits

- Thank you to [Gui-Yom](https://github.com/Gui-Yom) for writing hlbc and for maintaining documentation on the HashLink bytecode format, as well as for providing tests and helping me during development.
Expand Down
7 changes: 4 additions & 3 deletions crashlink/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@
```py
from crashlink import *
code = Bytecode.from_path("path/to/file.hl")
for func in code.functions:
if func.findex.value == 22 or func.findex.value == 240: # typical entry points that the compiler generates
print(disasm.func(code, func))
if code.fn(22): # 22 and 240 are typical entry points for the compiler to generate
print(disasm.func(code.fn(22)))
elif code.fn(240):
print(disasm.func(code.fn(240)))
# > f@22 static $Clazz.main () -> Void (from Clazz.hx)
# > Reg types:
# > 0. Void
Expand Down
30 changes: 25 additions & 5 deletions crashlink/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ class gIndex(ResolvableVarInt):

def resolve(self, code: "Bytecode") -> "Type":
return code.global_types[self.value].resolve(code)

def resolve_str(self, code: "Bytecode") -> str:
return code.const_str(self.value)


class strRef(ResolvableVarInt):
Expand Down Expand Up @@ -1484,6 +1487,7 @@ def deserialise(self, f: BinaryIO | BytesIO, search_magic: bool = True) -> "Byte
return self

def init_globals(self) -> None:
final: Dict[int, Any] = {}
for const in self.constants:
res: Dict[str, Any] = {}
obj = const._global.resolve(self).definition
Expand All @@ -1503,11 +1507,11 @@ def init_globals(self) -> None:
elif isinstance(typ, F32) or isinstance(typ, F64):
res[name] = self.floats[field.value].value
elif isinstance(typ, Bytes):
if self.version.value >= 5 and self.bytes:
print("WARNING: Using bytes for constant. This may be incorrect.")
res[name] = self.bytes.value[field.value] # TODO: verify that this is correct behaviour for >=5
else:
res[name] = self.strings.value[field.value]
res[name] = self.strings.value[field.value]
else:
res[name] = field.value
final[const._global.value] = res
self.initialized_globals = final

def fn(self, findex: int, native: bool = True) -> Function | Native:
for f in self.functions:
Expand All @@ -1527,6 +1531,22 @@ def g(self, gindex: int) -> tIndex:
if g.value == gindex:
return g
raise ValueError(f"Global {gindex} not found!")

def const_str(self, gindex: int) -> str:
# TODO: is this overcomplicated?
if gindex not in self.initialized_globals:
if gindex < 0 or gindex >= len(self.global_types):
raise ValueError(f"Global {gindex} not found!")
raise ValueError(f"Global {gindex} does not have a constant value!")
obj = self.global_types[gindex].resolve(self).definition
if not isinstance(obj, Obj):
raise TypeError(f"Global {gindex} is not an object!")
if not obj.name.resolve(self) == "String":
raise TypeError(f"Global {gindex} is not a string!")
obj_fields = obj.resolve_fields(self)
if len(obj_fields) != 2:
raise ValueError(f"Global {gindex} seems malformed!")
return self.initialized_globals[gindex][obj_fields[0].name.resolve(self)]

def serialise(self, auto_set_meta: bool = True) -> bytes:
start_time = datetime.now()
Expand Down
2 changes: 1 addition & 1 deletion crashlink/decomp.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ def __init__(self, code: Bytecode, filename: gIndex, line: int, class_name: gInd
self.method_name = method_name

def __repr__(self) -> str:
return f"<IRTrace>" # TODO: resolve globals
return f"<IRTrace: {self.filename.resolve_str(self.code)} {self.line} {self.class_name.resolve_str(self.code)} {self.method_name.resolve_str(self.code)}>"


class IRFunction:
Expand Down

0 comments on commit 723cbb2

Please sign in to comment.