Skip to content
bizzehdee edited this page Jun 23, 2026 · 1 revision

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.

Compiling

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);

Executing

engine.Run(program);

Run throws JITException / ScriptException on errors; they are not silently swallowed (unlike Execute).

Saving to disk

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");

Loading from disk

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.

Optimisations

The bytecode compiler applies several peephole optimisations automatically:

Optimisation Description
Constant folding 2 + 35 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

Source maps

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.

Engine state serialisation

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.

Clone this wiki locally