Skip to content

Session Management

A DecompilerSession owns one native bridge session. Creating a session initializes the decompiler library, resolves the runtime data directory, and enumerates available language/compiler pairs. Reusing a session across many decompilation calls avoids repeating that startup work on every call.

This guide covers the two main usage patterns.

Why sessions matter

Each DecompilerSession performs one-time decompiler library initialization, runtime data directory resolution, and language/compiler pair enumeration. Without a session, each one-shot call repeats that work. A long-lived session pays the cost once and reuses it across every subsequent call.

Sessions also provide deterministic resource cleanup. Closing a session explicitly, or via a context manager, releases bridge resources immediately rather than waiting for the garbage collector.

For most use cases, the with statement is the right tool. It guarantees the session is closed even if an exception is raised inside the block.

from flatline import DecompilerSession, DecompileRequest

request = DecompileRequest(
    memory_image=b"\x55\x48\x89\xe5\xb8\x01\x00\x00\x00\x5d\xc3",
    base_address=0x1000,
    function_address=0x1000,
    language_id="x86:LE:64:default",
    compiler_spec="gcc",
)

with DecompilerSession() as session:
    result = session.decompile_function(request)

print(result.c_code)
# The session is closed here, native resources released.

Batch work: one session, many calls

When decompiling multiple functions, open the session once and call decompile_function repeatedly. All calls share the same bridge session and skip redundant startup work.

from flatline import DecompilerSession, DecompileRequest

# Suppose you have a list of (base_address, function_address, code_bytes) tuples.
targets = [
    (0x1000, 0x1000, b"\x55\x48\x89\xe5\xb8\x2a\x00\x00\x00\x5d\xc3"),
    (0x2000, 0x2000, b"\x55\x48\x89\xe5\x31\xc0\x5d\xc3"),
    (0x3000, 0x3000, b"\x55\x48\x89\xe5\xb8\x00\x00\x00\x00\x5d\xc3"),
]

with DecompilerSession() as session:
    for base, entry, code in targets:
        request = DecompileRequest(
            memory_image=code,
            base_address=base,
            function_address=entry,
            language_id="x86:LE:64:default",
            compiler_spec="gcc",
        )
        result = session.decompile_function(request)
        if result.error is None:
            print(f"0x{entry:x}: {result.c_code}")
        else:
            print(f"0x{entry:x}: error — {result.error.message}")

Manual lifecycle: open, use, close

If a context manager is not practical (for example, in a class that holds a session as an attribute), manage the lifecycle explicitly with try/finally to ensure close() is always called.

from flatline import DecompilerSession

session = DecompilerSession()
try:
    pairs = session.list_language_compilers()
    result = session.decompile_function(request)
finally:
    session.close()

Checking session state

The is_closed property reports whether close() has been called. This is useful for defensive checks in long-lived objects.

session = DecompilerSession()
print(session.is_closed)  # False

session.close()
print(session.is_closed)  # True

Calling close() on an already-closed session is safe — it is a no-op. Calling decompile_function() or list_language_compilers() on a closed session raises InvalidArgumentError.

Session and runtime data

A single session is bound to the runtime data directory it was opened with. You can decompile functions targeting different ISAs within the same session as long as they use the same runtime data. If you need a different runtime data directory, create a separate session.