# 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 ```c 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`: ```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 ```c // moduleMything.h extern scriptproto_t MODULE_MYTHING_PROTO; // Init and dispose for the module itself: void moduleMyThingInit(void); void moduleMyThingDispose(void); ``` ### 2. Implement ```c // 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 ```c 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 ```c // 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//mymod.d.ts` - Add `/// ` to `types/index.d.ts`