-
-
Notifications
You must be signed in to change notification settings - Fork 5
Bytecode
DScript compiles source code to a platform-independent bytecode (Chunk) before execution. The compiler runs a peephole optimiser and the chunk can be serialised to disk with an embedded source map.
ScriptEngine.Compile is a static method — it does not require an engine instance and the resulting Chunk can be shared across multiple engines.
var program = ScriptEngine.Compile(@"
function fib(n) {
return n <= 1 ? n : fib(n-1) + fib(n-2);
}
var result = fib(10);
");Compile once, run many times:
engine1.Run(program);
engine2.Run(program);engine.Run(program);Run throws JITException / ScriptException on errors; they are not silently swallowed (unlike Execute).
using DScript.Vm;
// With source map — enables line-accurate stack traces after reload
BytecodeSerializer.SaveWithSourceMap(program, "app.dsc");
// Without source map (smaller file)
BytecodeSerializer.Save(program, "app.dsc");var loaded = BytecodeSerializer.LoadWithSourceMap("app.dsc");
// or
var loaded = BytecodeSerializer.Load("app.dsc");
engine.Run(loaded);Native functions are resolved by name at run time. The bytecode does not embed native implementations — the host must register the same natives before calling Run.
The bytecode compiler applies several peephole optimisations automatically:
| Optimisation | Description |
|---|---|
| Constant folding |
2 + 3 → 5 at compile time |
| BinaryIntConst fusion |
x + 1, x * 2 emit a fused instruction |
| Jump-chain collapse | Chains of unconditional jumps are collapsed to a single target |
| Dead-code elimination | Code after unconditional return/break/continue is removed |
| Tail call optimisation |
return f(...) in tail position avoids growing the C# call stack |
A source map embeds the mapping from bytecode offset to source file and line number. This enables:
- Accurate line numbers in
JITException.ScriptStackTrace - Breakpoints by source line in the step debugger
- Hover and go-to-definition in the Language Server
When loading bytecode saved without a source map, stack traces show offsets only.
Separate from bytecode, you can snapshot all script-side global variables and restore them on a fresh engine:
// Save
byte[] snapshot = engine.SerializeState();
// Restore (must register the same natives first)
var fresh = new ScriptEngine();
new EngineFunctionLoader().RegisterFunctions(fresh);
fresh.DeserializeState(snapshot);State serialisation captures every global variable value but does not capture compiled code — you must re-run or re-load the original program before accessing functions or prototypes defined in it.