4.9 KiB
Script System (JerryScript)
Source: src/dusk/script/, modules at src/dusk/script/module/
Overview
The engine embeds JerryScript as its scripting runtime. Game scenes and logic are authored in JavaScript (ES5 subset). The script system initialises JerryScript, registers all built-in C modules as JS globals, and runs the event loop each tick.
The full rules for writing JS asset scripts are in CLAUDE.md under
"JavaScript (asset scripts)". This doc covers the C-side module system.
Script lifecycle
errorret_t scriptInit(); // start JerryScript, register all modules
errorret_t scriptUpdate(); // run pending microjobs (call once per frame)
errorret_t scriptDispose(); // shut down JerryScript
errorret_t scriptExecString(const char_t *source);
// Evaluate a JS source string in global scope.
errorret_t scriptExecFile(const char_t *path);
// Load + eval a script from the asset archive. Result cached by asset
// system -- repeated calls with the same path do not re-execute.
Module registration
All C modules are initialised in src/dusk/script/module/modulelist.c:
void moduleListInit(void); // called by scriptInit
void moduleListDispose(void); // called by scriptDispose
Each module's Init is called once. The module registers its
properties and methods on scriptproto_t objects (see below), which
become JS globals.
Writing a C module -- the scriptproto_t pattern
A scriptproto_t represents a JS class prototype backed by a C struct.
1. Declare in the header
// moduleMything.h
extern scriptproto_t MODULE_MYTHING_PROTO;
// Init and dispose for the module itself:
void moduleMyThingInit(void);
void moduleMyThingDispose(void);
2. Implement
// moduleMything.c
scriptproto_t MODULE_MYTHING_PROTO;
// JS-callable function using the convenience macro:
moduleBaseFunction(myThingDoSomething) {
moduleBaseRequireArgs(1);
moduleBaseRequireNumber(0);
float_t x = moduleBaseArgFloat(0);
// ... do work ...
return jerry_undefined();
}
void moduleMyThingInit(void) {
scriptProtoInit(
&MODULE_MYTHING_PROTO,
"MyThing", // JS global name; NULL to skip registration
sizeof(mything_t),
myThingCtor // constructor handler, or NULL
);
// Instance methods:
scriptProtoDefineFunc(
&MODULE_MYTHING_PROTO, "doSomething", myThingDoSomething
);
// Instance property (get/set):
scriptProtoDefineProp(
&MODULE_MYTHING_PROTO, "x", myThingGetX, myThingSetX
);
// Static method:
scriptProtoDefineStaticFunc(
&MODULE_MYTHING_PROTO, "create", myThingCreate
);
}
3. Register
In modulelist.c: #include the header and call moduleMyThingInit()
in moduleListInit() (and Dispose in moduleListDispose()).
moduleBaseFunction macro
moduleBaseFunction(myFn) {
// callInfo, args[], argc available
moduleBaseRequireArgs(2);
moduleBaseRequireNumber(0);
moduleBaseRequireString(1);
float_t x = moduleBaseArgFloat(0);
int32_t n = moduleBaseArgInt(0);
bool_t b = moduleBaseArgBool(0);
float_t opt = moduleBaseOptFloat(2, 0.0f); // optional with default
// Error propagation:
errorret_t ret = someCall();
if(errorIsNotOk(ret)) return moduleBaseThrowError(ret);
return jerry_undefined(); // or jerry_boolean(true) etc.
}
Wrapping C values in JS objects
// Create a JS object wrapping a copy of a C value:
jerry_value_t obj = scriptProtoCreateValue(&MY_PROTO, &myValue);
// Unwrap back to C pointer:
mything_t *ptr = scriptProtoGetValue(&MY_PROTO, jsObj);
// ptr is NULL if jsObj is not an instance of MY_PROTO.
Utility helpers (modulebase.h)
| Helper | Purpose |
|---|---|
moduleBaseThrow(msg) |
Return a JS TypeError |
moduleBaseThrowError(ret) |
Convert errorret_t -> JS error |
moduleBaseToString(val, buf, len) |
Jerry value -> C string |
moduleBaseGetProp(obj, name) |
Get object property by name |
moduleBaseWrapPointer(ptr) |
Wrap a raw pointer in a JS object |
moduleBaseUnwrapPointer(val) |
Unwrap a raw pointer |
moduleBaseSetValue(name, val) |
Set a global JS variable |
moduleBaseSetNumber(name, n) |
Set a global JS number |
moduleBaseSetInt(name, n) |
Set a global JS integer |
moduleBaseDefineMethod(obj, name, fn) |
Add method to any JS object |
moduleBaseDefineGlobalMethod(name, fn) |
Add method to global scope |
Async JS -- pending promises (scriptpromisepend.h)
When a C module needs to resolve a JS Promise from an asynchronous
C event, use scriptpromisepend_t. Each module declares a fixed-size
pending slot array; the helpers add/resolve/reject by an opaque key.
Full API and design notes: .claude/script-promises.md
Type declarations (.d.ts)
Every module that is accessible from JS must have a corresponding
TypeScript declaration file in types/. The CLAUDE.md checklist
requires updating these whenever a .c module file changes.
- Add
types/<category>/mymod.d.ts - Add
/// <reference path="..." />totypes/index.d.ts