⚙️ Compile subsystem — HIX_Compile*¶
HIX exposes a unified subsystem for compiling and executing dynamic Harbour
code from within your application. It is the path used internally by the
dispatcher (dynamically loaded .prg files), loaders, middlewares and the
view engine. It is also available for your own controllers when you need to
compile or execute a .prg or a pre-compiled HRB at response time.
🧭 Introduced in v2.3.12, consolidated in v2.4.0 as the only compilation path in the lib. The former
HIX_CompileFilewas removed in v2.4.0.
Why a subsystem¶
Before v2.3.12 there were two separate paths: one for pure compile
(HIX_CompileFile + hb_CompileFromBuf) and another for compile+run, with
different error contracts and stack walkers specific to each caller. The
result: duplicated code, hard-to-propagate errors and asymmetric boot logs
between loaders and dispatcher.
v2.4.0 consolidates all those paths (dispatcher, loaders, middleware loader, view engine) on a single hash contract:
ok— boolean,.T.if the final phase produced no error.phase— which phase execution stopped at:"compile","load","run".hrb— bytes of the HRB resulting from compile (if compile phase passed).err— normalised error hash (see below).NILifok=.T..diagnostics— array of secondary diagnostics (warnings, lines, ...).preambleLines— number of preamble lines injected (adjustslineinerr).where— compile location info (filename, etc.).
Public API¶
All functions never propagate Break: they return the hash with
ok=.F. and err populated if something fails. You never need to wrap
them in TRY/CATCH.
HIX_CompileCode( cSource [, hOpts] [, cFilename] )¶
Compiles a .prg source in memory. Typical options in hOpts:
{ "virtualName" => "my_module", "runtime" => .F. }. cFilename
labels the error when compile fails.
LOCAL hRes := HIX_CompileCode( "FUNCTION Foo() ; RETURN 42" )
IF ! hRes["ok"]
le( "Compile failed: " + hRes["err"]["description"] )
RETURN NIL
ENDIF
// hRes["hrb"] contains the HRB ready to execute
HIX_CompileCodeFromFile( cPath )¶
Compile-only wrapper from disk. Returns the same hash. This is the path
used by the dispatcher and loaders to compile persisted .prg files.
LOCAL hRes := HIX_CompileCodeFromFile( "www/controllers/my_action.prg" )
IF ! hRes["ok"]
USendError( 500, "Compile: " + hRes["err"]["description"] )
RETURN NIL
ENDIF
HIX_CompileRunPrg( cPath [, ...args] )¶
Compile + execute in one step. Also captures runtime errors, not just
compile errors. Returns the same contract hash — phase will be
"compile" if it failed while compiling and "run" if it failed
while executing.
LOCAL hRes := HIX_CompileRunPrg( "scripts/report.prg", nUserId, cPeriod )
IF ! hRes["ok"]
// Same shape whether it was a compile or runtime error
USendError( 500, hRes["err"]["description"] )
RETURN NIL
ENDIF
HIX_CompileRunHrb( cHrbBytes [, cVirtualName [, ...args]] )¶
Executes an already compiled HRB (bytes). Useful when you have the .hrb
in cache or generated it with HIX_CompileCode and saved it to disk.
Internally uses HB_HRB_BIND_FORCELOCAL to avoid collisions with other
loaded HRBs.
LOCAL cHrb := hb_MemoRead( "cache/report.hrb" )
LOCAL hRes := HIX_CompileRunHrb( cHrb, "report", nUserId )
HIX_CompileLastError()¶
Returns a snapshot of the last runtime error captured by the subsystem.
THREAD STATIC — a thread only sees its own errors. Useful when you
have called HIX_CompileRunPrg and need the full stack apart from the
err already in the hash.
LOCAL hLast := HIX_CompileLastError()
IF hLast != NIL
ld( "aStack: " + hb_ValToExp( hLast["aStack"] ) )
ENDIF
Normalised err shape¶
The error hash inside hRes["err"] always has the same keys, whether from
compile or runtime. Compile produces the subset:
| Key | Meaning |
|---|---|
kind |
"compile" or "runtime" |
severity |
"error", "warning" |
code |
Numeric code (preprocessor, parser) |
description |
Main message — ready to show to the user |
file |
Source file where the error was detected |
line |
Line (already adjusted by preambleLines) |
lineCode |
Exact snippet of the failing line |
Runtime adds full Harbour context:
| Additional key | Meaning |
|---|---|
operation |
Function/operation that failed |
args |
Arguments passed when it crashed |
subsystem |
oError:subSystem |
genCode |
oError:genCode (generic Harbour code) |
subCode |
oError:subCode |
osCode |
OS code if applicable |
filename |
Runtime error file |
stack |
Stack trace as text |
aStack |
Stack trace as structured array |
Example — controller executing a dynamic script¶
FUNCTION _RunUserScript()
LOCAL cPath := "www/scripts/" + UParam( "name" ) + ".prg"
LOCAL hRes
IF ! hb_FileExists( cPath )
RETURN USendError( 404, "Script not found" )
ENDIF
hRes := HIX_CompileRunPrg( cPath, USession( "user_id" ) )
IF ! hRes["ok"]
le( "Script failed [" + hRes["phase"] + "]: " + hRes["err"]["description"] )
RETURN USendJson( { ;
"ok" => .F., ;
"phase" => hRes["phase"], ;
"error" => hRes["err"]["description"],;
"line" => hRes["err"]["line"] ;
}, 500 )
ENDIF
USendJson( { "ok" => .T. } )
RETURN NIL
Enriched boot log¶
From v2.4.0 onwards, when a loader or middleware fails to compile
during startup, the boot log dumps line, code and snippet instead of just
the message. No configuration required — automatic because the loader uses
HIX_CompileCodeFromFile and translates its hash directly to the logger.
Typical format:
[loader] fail: www/loaders/stats.prg
line: 42 (code: 2007)
>> LOCAL x = y + z // = instead of :=
description: Syntax error
When not to use it¶
- Do not call
HIX_CompileRunPrgin your hot path for scripts that never change — it compiles every time. Save the HRB withHIX_CompileCode - an
.md5as cache and useHIX_CompileRunHrbafterwards. That is exactly what the view engine does internally. - Do not use this subsystem to evaluate simple expressions. For that
Harbour already has
&(cExpr)(macro-eval). Compile is designed for full.prgblocks withFUNCTION/PROCEDURE. - Do not rely on
errcarryingaStackwhenkind=="compile". The stack only makes sense for runtime errors.
Changes from previous versions¶
| Before v2.3.12 | From v2.4.0 onwards |
|---|---|
HIX_CompileFile( cPath ) |
Removed. Use HIX_CompileCodeFromFile( cPath ) for compile-only or HIX_CompileRunPrg( cPath ) for compile+run. |
hb_CompileFromBuf direct |
Encapsulated in HIX_CompileFromBuf (low-level, internal use of HIX_CompileCode) |
oError:operation == "line:N" |
oError:operation == "line N". Official source is oErr:cargo["line"] |
src/hix_prepro.prg |
Removed. TRY/CATCH/FINALLY directives now live in src/include/hix_const.ch. |
See changes/2.4.0.md for the complete list of breaking changes and the migration audit trail.