Skip to content
bizzehdee edited this page Jun 25, 2026 · 4 revisions

Engine

ScriptEngine is the central object. It holds the global variable environment, the native function registry, and all runtime state.

Creating an engine

using DScript;
using DScript.Extras;

var engine = new ScriptEngine();

// Register the DScript.Extras standard library (optional but recommended)
new EngineFunctionLoader().RegisterFunctions(engine);

To restrict system access, pass an EnginePermissions value — see Permissions.

new EngineFunctionLoader().RegisterFunctions(engine, EnginePermissions.FileSystem);

Running code

Execute

engine.Execute("var x = 6 * 7;");

Execute compiles and runs the code in one step. ScriptException and JITException are caught internally and written to Console.Error; all other exceptions propagate. Use Run when you want exceptions to propagate.

Run

try
{
    engine.Run(ScriptEngine.Compile("var x = undeclaredFn();"));
}
catch (JITException ex)
{
    Console.Error.WriteLine(ex.ToString()); // includes script stack trace
}

Compile once, run many times

ScriptEngine.Compile is a static method — the resulting Chunk is engine-independent and can be shared or cached.

var program = ScriptEngine.Compile("var answer = 6 * 7;");

engine.Run(program); // fast — no re-parsing

EvalComplex

Evaluate a single expression and return its value as a ScriptVarLink.

var link = engine.EvalComplex("2 + 2");
Console.WriteLine(link.Var.Int); // 4

EnableOptimizer

Controls whether the bytecode optimiser runs when the engine compiles source (constant folding, dead-code elimination, super-instruction fusion, narrow encoding). It is true by default and is honoured by Execute, EvalComplex, and module compilation. Set it to false to compile straight, unoptimised bytecode — handy for inspecting or debugging codegen. Results are identical either way; only the emitted bytecode differs.

var engine = new ScriptEngine { EnableOptimizer = false };
engine.Execute("var x = 2 + 3 * 4;"); // compiled without constant folding

MaxCallStackDepth

Bounds nested script-function recursion (default 10000). Exceeding it throws a catchable "Maximum call stack size exceeded" error instead of letting the native .NET stack overflow (which would crash the process). Tail calls are trampolined and don't count toward the depth. See Resource Limits for details.

var engine = new ScriptEngine { MaxCallStackDepth = 5000 };

Reading variables

After execution, global variables are accessible via engine.Root:

engine.Execute("var name = 'Alice'; var age = 30;");

string name = engine.Root.GetParameter("name").String; // "Alice"
int    age  = engine.Root.GetParameter("age").Int;     // 30

GetParameter returns a ScriptVar. Use .Int, .String, .Double, .Bool, or .IsUndefined / .IsNull to read its value.

Calling script functions from C#

engine.Execute("function square(n) { return n * n; }");

var fn     = engine.Root.GetParameter("square");
var result = engine.CallFunction(fn, null, new ScriptVar(9));

Console.WriteLine(result.Int); // 81

CallFunction(fn, thisArg, params...) accepts any number of ScriptVar arguments. Pass null for thisArg to use the global scope.

Registering native functions

engine.AddNative("function add(a, b)", (scope, userData) =>
{
    int a = scope.GetParameter("a").Int;
    int b = scope.GetParameter("b").Int;
    scope.ReturnVar.Int = a + b;
}, null);

engine.Execute("console.log(add(3, 4));"); // 7

The first argument is a function signature string — the parameter names declared here are how values are retrieved inside the callback. userData is the third argument passed to AddNative and is forwarded to every invocation.

State serialisation

Save all script-side variables to a byte array and restore them later on a fresh engine (natives must be re-registered first):

engine.Execute("var counter = 42;");
byte[] snapshot = engine.SerializeState();

var engine2 = new ScriptEngine();
new EngineFunctionLoader().RegisterFunctions(engine2);
engine2.DeserializeState(snapshot);

Console.WriteLine(engine2.Root.GetParameter("counter").Int); // 42

Module loader

Supply a callback to resolve require() and import paths:

engine.ModuleLoader = (path, fromPath) =>
    File.ReadAllText(Path.Combine(baseDir, path + ".ds"));

See Modules for the full module system.

ScriptVar

ScriptVar represents any DScript value — number, string, boolean, object, array, function, null, or undefined.

Member Description
new ScriptVar(int) Create an integer value
new ScriptVar(double) Create a floating-point value
new ScriptVar(string) Create a string value
new ScriptVar(bool) Create a boolean value
new ScriptVar(ScriptVar.Flags.Null) Create a null value
new ScriptVar(ScriptVar.Flags.Undefined) Create undefined
.Int Read/write as integer
.Double Read/write as double
.String Read/write as string
.Bool Read as boolean (non-zero / non-empty)
.IsNull True if the value is null
.IsUndefined True if the value is undefined
.IsArray True if the value is an array
.IsFunction True if the value is a function
.GetArrayLength() Number of array elements
.GetArrayIndex(i) Get element at index i
.SetArrayIndex(i, val) Set element at index i
.FindChild(name) Find a named property (returns ScriptVarLink?)
.AddChild(name, val) Add a named property
.GetParsableString() Stringify the value (for display)
.SetData(obj) / .GetData() Attach/retrieve a native CLR object

Exceptions

Type When thrown
JITException Runtime error in script code (carries .ScriptStackTrace)
ScriptException Explicit throw in script, or native code calling throw new ScriptException(msg)
ScriptTimeoutException Wall-clock or instruction limit exceeded — cannot be caught by script try/catch
PermissionException Script attempted an operation denied by EnginePermissions

Clone this wiki locally