tbcx — serialize, load, and inspect precompiled Tcl 9.1 bytecode (procs, OO methods, and lambdas). Artifacts require an exact Tcl major/minor match at load time.
tbcx::save in out
tbcx::load in
tbcx::dump filename
tbcx::gc
The tbcx extension provides four commands that enable an efficient save → load → eval pipeline for Tcl 9.1 scripts.
The goal is to pay the cost of parsing/compiling at save time so that loading is as fast as reading a compact binary, while remaining functionally equivalent to source of the original script.
Artifacts store the compiled top-level, precompiled proc bodies, TclOO method/ctor/dtor bodies, and lambda literals for use with apply. Loading installs these into the current interpreter and executes the top-level block with TCL_EVAL_GLOBAL.
In safe interpreters, tbcx_SafeInit provides the package and type infrastructure but does not register any tbcx::* commands. A parent interpreter may selectively grant access with interp alias or interp expose.
Synopsis — Compile a script and write a .tbcx artifact.
in — Resolved in this order:
out — One of:
-translation binary -eofchar {}) is enforced. The caller’s channel settings are mutated and not restored. The channel is not closed.Examples
# Save from path → path
set path [tbcx::save ./app.tcl ./app.tbcx]
# Save from string value → path
set script {proc hi {} {puts Hello}; hi}
tbcx::save $script ./hello.tbcx
# Save from channel → channel
set in [open ./lib/foo.tcl r]
set out [open ./foo.tbcx w]
fconfigure $out -translation binary -eofchar {}
try {
tbcx::save $in $out
} finally {
close $in
close $out
}
Synopsis — Load a .tbcx artifact, install precompiled entities, and execute the top-level block.
in — One of:
.tbcx stream (binary)..tbcx file.Examples
# Load into current interp
tbcx::load ./app.tbcx
# Load from an open channel
set ch [open ./app.tbcx r]
fconfigure $ch -translation binary -eofchar {}
try {
tbcx::load $ch
} finally {
close $ch
}
Synopsis — Disassemble and describe a .tbcx artifact in human-readable form.
.tbcx file.Synopsis — Purge stale entries from the per-interpreter lambda shimmer-recovery registry.
tbcx::gc is a no-op if no ApplyShim has been installed yet (i.e. before any tbcx::load call), and it is safe to call multiple times.
This section summarizes the on-disk structure. Format version is 91 (Tcl 9.1). All integers are little-endian.
0x58434254) + format version (91) + producing Tcl version;
size/count metadata for the top-level block (code length, exception ranges,
literal count, AuxData count, locals, max stack).
Literals in the script that represent lambdas for apply (lists of the form
{args body ?ns?}) are compiled and serialized as lambda-bytecode literals at
save time. On load, the compiled body, argument list, and optional namespace element are
rehydrated into a Proc and registered in the ApplyShim so that the first
call to apply does not trigger compilation. If type shimmer later evicts the
lambdaExpr internal representation, the ApplyShim transparently re-installs it on the
next apply call.
Artifacts are portable across little- and big-endian hosts. The loader detects host byte order once and applies a tight in-place swap over bulk sections that require conversion, avoiding per-field branching so cross-endian loads remain close to native performance.
Loading executes the precompiled top-level (with TCL_EVAL_GLOBAL), then installs precompiled proc/method bodies and rehydrates lambda literals. The intent is to be functionally indistinguishable from source of the original script, with the benefit of faster startup due to avoided parsing/compilation.
TBCX precompiles bodies and lambdas only when they are present in statically identifiable literal positions (script-body arguments to commands like foreach, while, try, eval, etc., or lambda literals for apply). Strings assembled at runtime — for example with format, string interpolation, or list construction — still round-trip correctly, but they remain ordinary data and compile at execution time when Tcl evaluates them.
TBCX preserves normal TclOO class/object construction semantics by executing the rewritten top-level script, while substituting precompiled bodies for recognized oo::define / oo::objdefine method forms. Tested scenarios include class methods, self methods, per-object methods, private methods, inheritance (including diamond), mixins, filters, forwards, abstract/singleton metaclasses, method rename/delete/export changes, metaclasses with self method, and next-based constructor chaining.
Artifacts are designed to load into interpreters other than the originating one. The bundled tests cover child interpreters and threaded scenarios. Interpreter-specific state such as the ApplyShim lambda registry remains per-interpreter.
Sanity caps exist for code size (64 MiB), literal/AuxData/exception counts (1M each), string lengths (4 MiB), total serialized output (256 MB), serialization recursion depth (64), total WriteLiteral calls (2M), and total WriteCompiledBlock calls (256K). Exceeding any limit produces an error.
Nested or reentrant tbcx::load calls are capped at depth 8 per interpreter to prevent runaway recursive loading.
Representative messages include: “bad header”, “incompatible Tcl version”, “short read/write”, “unsupported AuxData kind”, “input is neither an open channel nor a readable file”, “runaway serialization detected”, and Tcl errors from top-level evaluation.
Loading executes code. Only load artifacts you trust. Safe interpreters receive no tbcx::* commands by default; use interp alias or interp expose from a parent interpreter to grant selective access.
source(n), TclOO(n)
© 2025–2026 Miguel Banon
MIT License.