Restoring JerryScript a bit cleaner

This commit is contained in:
2026-06-01 21:52:36 -05:00
parent 41a4be678e
commit 8f78bba9e9
27 changed files with 1459 additions and 4 deletions
+12
View File
@@ -0,0 +1,12 @@
Console.print('This is called from JavaScript');
const platformNames = {
[System.PLATFORM_LINUX]: 'Linux',
[System.PLATFORM_KNULLI]: 'Knulli',
[System.PLATFORM_PSP]: 'PSP',
[System.PLATFORM_GAMECUBE]: 'GameCube',
[System.PLATFORM_WII]: 'Wii',
};
const platformName = platformNames[System.platform] || 'Unknown';
Console.print('Platform: ' + platformName);
+96
View File
@@ -0,0 +1,96 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Turn things off we don't need
set(JERRY_CMDLINE OFF CACHE BOOL "" FORCE)
set(JERRY_EXT ON CACHE BOOL "" FORCE)
set(JERRY_DEBUGGER OFF CACHE BOOL "" FORCE)
set(JERRY_BUILTIN_DATE OFF CACHE BOOL "" FORCE)
set(ENABLE_LTO OFF CACHE BOOL "" FORCE)
# Fetch Jerry
include(FetchContent)
FetchContent_Declare(
jerryscript
GIT_REPOSITORY https://git.wish.moe/YourWishes/jerryscript
GIT_TAG float32-fix
)
FetchContent_MakeAvailable(jerryscript)
# Mark found
set(jerryscript_FOUND ON)
# Define targets
if(TARGET jerryscript-core)
set(JERRY_CORE_TARGET jerryscript-core)
elseif(TARGET jerry-core)
set(JERRY_CORE_TARGET jerry-core)
endif()
if(TARGET jerryscript-ext)
set(JERRY_EXT_TARGET jerryscript-ext)
elseif(TARGET jerry-ext)
set(JERRY_EXT_TARGET jerry-ext)
endif()
if(TARGET jerryscript-port-default)
set(JERRY_PORT_TARGET jerryscript-port-default)
elseif(TARGET jerry-port-default)
set(JERRY_PORT_TARGET jerry-port-default)
elseif(TARGET jerryscript-port)
set(JERRY_PORT_TARGET jerryscript-port)
elseif(TARGET jerry-port)
set(JERRY_PORT_TARGET jerry-port)
endif()
if(NOT JERRY_CORE_TARGET)
message(FATAL_ERROR "JerryScript core target not found")
endif()
if(NOT JERRY_EXT_TARGET)
message(FATAL_ERROR "JerryScript ext target not found")
endif()
if(NOT JERRY_PORT_TARGET)
message(FATAL_ERROR "JerryScript port target not found")
endif()
foreach(tgt IN ITEMS
${JERRY_CORE_TARGET}
${JERRY_EXT_TARGET}
${JERRY_PORT_TARGET}
)
if(TARGET ${tgt})
set_property(TARGET ${tgt} PROPERTY INTERPROCEDURAL_OPTIMIZATION OFF)
target_compile_definitions(${JERRY_CORE_TARGET} PRIVATE
JERRY_NUMBER_TYPE_FLOAT64=0
JERRY_BUILTIN_DATE=0
)
endif()
endforeach()
# Export include dirs through the targets
target_include_directories(${JERRY_CORE_TARGET} INTERFACE
${jerryscript_SOURCE_DIR}/jerry-core/include
)
target_include_directories(${JERRY_EXT_TARGET} INTERFACE
${jerryscript_SOURCE_DIR}/jerry-ext/include
)
target_include_directories(${JERRY_PORT_TARGET} INTERFACE
${jerryscript_SOURCE_DIR}/jerry-port/default/include
)
# Suppress JerryScript-only warning
if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(${JERRY_CORE_TARGET} PRIVATE
-Wno-error
)
endif()
add_library(jerryscript::core ALIAS ${JERRY_CORE_TARGET})
add_library(jerryscript::ext ALIAS ${JERRY_EXT_TARGET})
add_library(jerryscript::port ALIAS ${JERRY_PORT_TARGET})
+1
View File
@@ -34,6 +34,7 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_OPENGL
DUSK_OPENGL_ES
DUSK_LINUX
DUSK_KNULLI
DUSK_DISPLAY_SIZE_DYNAMIC
DUSK_DISPLAY_WIDTH_DEFAULT=640
DUSK_DISPLAY_HEIGHT_DEFAULT=480
+10
View File
@@ -32,6 +32,15 @@ if(NOT yyjson_FOUND)
endif()
endif()
if(NOT jerryscript_FOUND)
find_package(jerryscript REQUIRED)
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
jerryscript::core
jerryscript::ext
jerryscript::port
)
endif()
if(DUSK_BACKTRACE)
target_link_options(${DUSK_LIBRARY_TARGET_NAME} PUBLIC -rdynamic)
target_compile_definitions(${DUSK_BINARY_TARGET_NAME} PUBLIC
@@ -69,6 +78,7 @@ add_subdirectory(input)
add_subdirectory(locale)
add_subdirectory(physics)
add_subdirectory(scene)
add_subdirectory(script)
add_subdirectory(system)
add_subdirectory(time)
add_subdirectory(ui)
+2 -1
View File
@@ -14,4 +14,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
# Subdirs
add_subdirectory(display)
add_subdirectory(locale)
add_subdirectory(json)
add_subdirectory(json)
add_subdirectory(script)
+6
View File
@@ -39,4 +39,10 @@ assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = {
.loadAsync = assetJsonLoaderAsync,
.dispose = assetJsonDispose
},
[ASSET_LOADER_TYPE_SCRIPT] = {
.loadSync = assetScriptLoaderSync,
.loadAsync = assetScriptLoaderAsync,
.dispose = assetScriptDispose
},
};
+5
View File
@@ -11,6 +11,7 @@
#include "asset/loader/display/assettilesetloader.h"
#include "asset/loader/locale/assetlocaleloader.h"
#include "asset/loader/json/assetjsonloader.h"
#include "asset/loader/script/assetscriptloader.h"
typedef enum {
ASSET_LOADER_TYPE_NULL,
@@ -20,6 +21,7 @@ typedef enum {
ASSET_LOADER_TYPE_TILESET,
ASSET_LOADER_TYPE_LOCALE,
ASSET_LOADER_TYPE_JSON,
ASSET_LOADER_TYPE_SCRIPT,
ASSET_LOADER_TYPE_COUNT
} assetloadertype_t;
@@ -30,6 +32,7 @@ typedef union {
assettilesetloaderinput_t tileset;
assetlocaleloaderinput_t locale;
assetjsonloaderinput_t json;
assetscriptloaderinput_t script;
} assetloaderinput_t;
typedef union {
@@ -38,6 +41,7 @@ typedef union {
assettilesetloaderloading_t tileset;
assetlocaleloaderloading_t locale;
assetjsonloaderloading_t json;
assetscriptloaderloading_t script;
} assetloaderloading_t;
typedef union {
@@ -46,6 +50,7 @@ typedef union {
assettilesetoutput_t tileset;
assetlocaleoutput_t locale;
assetjsonoutput_t json;
assetscriptoutput_t script;
} assetloaderoutput_t;
typedef struct assetloading_s assetloading_t;
@@ -0,0 +1,9 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
assetscriptloader.c
)
@@ -0,0 +1,116 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assetscriptloader.h"
#include "asset/loader/assetloading.h"
#include "asset/loader/assetentry.h"
#include "asset/loader/assetloader.h"
#include "util/memory.h"
#include "assert/assert.h"
#include <jerryscript.h>
errorret_t assetScriptLoaderAsync(assetloading_t *loading) {
assertNotNull(loading, "Loading cannot be NULL");
if(loading->loading.script.state != ASSET_SCRIPT_LOADING_STATE_READ_FILE) {
errorOk();
}
assertNull(loading->loading.script.buffer, "Buffer already defined?");
assetfile_t *file = &loading->loading.script.file;
assetLoaderErrorChain(loading, assetFileInit(file, loading->entry->name, NULL, NULL));
assetLoaderErrorChain(loading, assetFileOpen(file));
size_t capacity = ASSET_SCRIPT_CHUNK_SIZE;
uint8_t *buffer = memoryAllocate(capacity + 1);
size_t offset = 0;
while(1) {
if(offset + ASSET_SCRIPT_CHUNK_SIZE > capacity) {
size_t oldCapacity = capacity + 1;
capacity += ASSET_SCRIPT_CHUNK_SIZE;
memoryResize((void **)&buffer, oldCapacity, capacity + 1);
}
assetLoaderErrorChain(loading, assetFileRead(
file, buffer + offset, ASSET_SCRIPT_CHUNK_SIZE
));
size_t chunk = (size_t)file->lastRead;
offset += chunk;
if(chunk == 0) break;
}
buffer[offset] = '\0';
assetLoaderErrorChain(loading, assetFileClose(file));
assetLoaderErrorChain(loading, assetFileDispose(file));
loading->loading.script.buffer = buffer;
loading->loading.script.size = offset;
loading->loading.script.state = ASSET_SCRIPT_LOADING_STATE_EXEC;
loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC;
errorOk();
}
errorret_t assetScriptLoaderSync(assetloading_t *loading) {
assertNotNull(loading, "Loading cannot be NULL");
assertTrue(loading->type == ASSET_LOADER_TYPE_SCRIPT, "Invalid type.");
switch(loading->loading.script.state) {
case ASSET_SCRIPT_LOADING_STATE_INITIAL:
loading->loading.script.state = ASSET_SCRIPT_LOADING_STATE_READ_FILE;
loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC;
errorOk();
break;
case ASSET_SCRIPT_LOADING_STATE_EXEC:
break;
default:
errorOk();
}
uint8_t *buffer = loading->loading.script.buffer;
assertNotNull(buffer, "Script buffer should have been loaded by now.");
jerry_value_t result = jerry_eval(
(const jerry_char_t *)buffer,
loading->loading.script.size,
JERRY_PARSE_NO_OPTS
);
memoryFree(buffer);
loading->loading.script.buffer = NULL;
if(jerry_value_is_exception(result)) {
jerry_value_t errVal = jerry_exception_value(result, false);
jerry_value_t errStr = jerry_value_to_string(errVal);
char_t buf[256];
jerry_size_t len = jerry_string_to_buffer(
errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, sizeof(buf) - 1
);
buf[len] = '\0';
jerry_value_free(errStr);
jerry_value_free(errVal);
jerry_value_free(result);
assetLoaderErrorThrow(loading, "Script error in '%s': %s",
loading->entry->name, buf
);
}
loading->entry->data.script = (assetscriptoutput_t)result;
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
errorOk();
}
errorret_t assetScriptDispose(assetentry_t *entry) {
assertNotNull(entry, "Asset entry cannot be NULL");
assertTrue(entry->type == ASSET_LOADER_TYPE_SCRIPT, "Invalid type.");
if(entry->data.script != 0) {
jerry_value_free((jerry_value_t)entry->data.script);
entry->data.script = 0;
}
errorOk();
}
@@ -0,0 +1,35 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "asset/assetfile.h"
#define ASSET_SCRIPT_CHUNK_SIZE 1024
typedef struct assetloading_s assetloading_t;
typedef struct assetentry_s assetentry_t;
typedef struct { void *nothing; } assetscriptloaderinput_t;
typedef uint32_t assetscriptoutput_t;
typedef enum {
ASSET_SCRIPT_LOADING_STATE_INITIAL,
ASSET_SCRIPT_LOADING_STATE_READ_FILE,
ASSET_SCRIPT_LOADING_STATE_EXEC,
ASSET_SCRIPT_LOADING_STATE_DONE
} assetscriptloadingstate_t;
typedef struct {
assetfile_t file;
assetscriptloadingstate_t state;
uint8_t *buffer;
size_t size;
} assetscriptloaderloading_t;
errorret_t assetScriptLoaderAsync(assetloading_t *loading);
errorret_t assetScriptLoaderSync(assetloading_t *loading);
errorret_t assetScriptDispose(assetentry_t *entry);
+5 -2
View File
@@ -23,6 +23,7 @@
#include "network/network.h"
#include "system/system.h"
#include "console/console.h"
#include "script/script.h"
#include "item/backpack.h"
#include "save/save.h"
@@ -54,9 +55,10 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
physicsManagerInit();
errorChain(networkInit());
/* Run the init script. */
errorChain(scriptInit());
errorChain(scriptExecFile("init.js"));
consolePrint("Engine initialized");
sceneSet(SCENE_TYPE_OVERWORLD);
errorOk();
}
@@ -96,6 +98,7 @@ errorret_t engineDispose(void) {
errorChain(displayDispose());
// errorChain(saveDispose());
errorChain(assetDispose());
errorChain(scriptDispose());
errorOk();
}
+14
View File
@@ -0,0 +1,14 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
script.c
scriptproto.c
)
# Subdirectories
add_subdirectory(module)
+9
View File
@@ -0,0 +1,9 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
modulebase.c
)
@@ -0,0 +1,68 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/module/modulebase.h"
#include "script/scriptproto.h"
#include "console/console.h"
static scriptproto_t MODULE_CONSOLE_PROTO;
moduleBaseFunction(moduleConsolePrint) {
char_t buf[512];
char_t msg[4096];
size_t msgLen = 0;
for(jerry_length_t i = 0; i < argc; ++i) {
jerry_value_t strVal = jerry_value_to_string(args[i]);
moduleBaseToString(strVal, buf, sizeof(buf));
jerry_value_free(strVal);
size_t partLen = strlen(buf);
if(msgLen + partLen + 1 < sizeof(msg)) {
stringCopy(msg + msgLen, buf, sizeof(msg) - msgLen);
msgLen += partLen;
}
if(i + 1 < argc && msgLen + 1 < sizeof(msg)) {
msg[msgLen++] = '\t';
msg[msgLen] = '\0';
}
}
consolePrint("%s", msg);
return jerry_undefined();
}
moduleBaseFunction(moduleConsoleGetVisible) {
return jerry_boolean(CONSOLE.visible);
}
moduleBaseFunction(moduleConsoleSetVisible) {
moduleBaseRequireArgs(1);
CONSOLE.visible = moduleBaseArgBool(0);
return jerry_undefined();
}
static void moduleConsoleInit(void) {
scriptProtoInit(
&MODULE_CONSOLE_PROTO, "Console",
sizeof(uint8_t), NULL
);
scriptProtoDefineStaticFunc(
&MODULE_CONSOLE_PROTO, "print", moduleConsolePrint
);
scriptProtoDefineStaticProp(
&MODULE_CONSOLE_PROTO, "visible",
moduleConsoleGetVisible, moduleConsoleSetVisible
);
}
static void moduleConsoleDispose(void) {
scriptProtoDispose(&MODULE_CONSOLE_PROTO);
}
@@ -0,0 +1,38 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/module/modulebase.h"
#include "script/scriptproto.h"
#include "engine/engine.h"
static scriptproto_t MODULE_ENGINE_PROTO;
moduleBaseFunction(moduleEngineGetRunning) {
return jerry_boolean(ENGINE.running);
}
moduleBaseFunction(moduleEngineExit) {
ENGINE.running = false;
return jerry_undefined();
}
static void moduleEngineInit(void) {
scriptProtoInit(&MODULE_ENGINE_PROTO, "Engine", sizeof(uint8_t), NULL);
scriptProtoDefineStaticProp(
&MODULE_ENGINE_PROTO, "running",
moduleEngineGetRunning, NULL
);
scriptProtoDefineStaticFunc(
&MODULE_ENGINE_PROTO, "exit", moduleEngineExit
);
}
static void moduleEngineDispose(void) {
scriptProtoDispose(&MODULE_ENGINE_PROTO);
}
+133
View File
@@ -0,0 +1,133 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/module/modulebase.h"
#include "script/scriptproto.h"
#include "input/input.h"
#include "input/inputbutton.h"
static scriptproto_t MODULE_INPUT_PROTO;
/** Validates an inputaction_t argument and returns a type error if bad. */
#define moduleInputRequireAction(i, ctx) do { \
if(!jerry_value_is_number(args[(i)])) { \
return moduleBaseThrow(ctx ": action must be a number"); \
} \
const inputaction_t _act = (inputaction_t)moduleBaseArgInt(i); \
if(_act <= INPUT_ACTION_NULL || _act >= INPUT_ACTION_COUNT) { \
return moduleBaseThrow(ctx ": invalid action"); \
} \
} while(0)
moduleBaseFunction(moduleInputIsDown) {
moduleInputRequireAction(0, "Input.isDown");
return jerry_boolean(
inputIsDown((inputaction_t)moduleBaseArgInt(0))
);
}
moduleBaseFunction(moduleInputWasDown) {
moduleInputRequireAction(0, "Input.wasDown");
return jerry_boolean(
inputWasDown((inputaction_t)moduleBaseArgInt(0))
);
}
moduleBaseFunction(moduleInputPressed) {
moduleInputRequireAction(0, "Input.pressed");
return jerry_boolean(
inputPressed((inputaction_t)moduleBaseArgInt(0))
);
}
moduleBaseFunction(moduleInputReleased) {
moduleInputRequireAction(0, "Input.released");
return jerry_boolean(
inputReleased((inputaction_t)moduleBaseArgInt(0))
);
}
moduleBaseFunction(moduleInputGetValue) {
moduleInputRequireAction(0, "Input.getValue");
return jerry_number(
inputGetCurrentValue((inputaction_t)moduleBaseArgInt(0))
);
}
moduleBaseFunction(moduleInputAxis) {
moduleInputRequireAction(0, "Input.axis");
moduleInputRequireAction(1, "Input.axis");
return jerry_number(inputAxis(
(inputaction_t)moduleBaseArgInt(0),
(inputaction_t)moduleBaseArgInt(1)
));
}
moduleBaseFunction(moduleInputBind) {
moduleBaseRequireArgs(2);
moduleBaseRequireString(0);
if(!jerry_value_is_number(args[1])) {
return moduleBaseThrow("Input.bind: action must be a number");
}
const inputaction_t action = (inputaction_t)moduleBaseArgInt(1);
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
return moduleBaseThrow("Input.bind: invalid action");
}
char_t name[128];
moduleBaseToString(args[0], name, sizeof(name));
if(name[0] == '\0') {
return moduleBaseThrow("Input.bind: button name cannot be empty");
}
inputbutton_t btn = inputButtonGetByName(name);
if(btn.type == INPUT_BUTTON_TYPE_NONE) {
return moduleBaseThrow("Input.bind: unknown button name");
}
inputBind(btn, action);
return jerry_undefined();
}
static void moduleInputInit(void) {
scriptProtoInit(&MODULE_INPUT_PROTO, "Input", sizeof(uint8_t), NULL);
scriptProtoDefineStaticFunc(
&MODULE_INPUT_PROTO, "isDown", moduleInputIsDown
);
scriptProtoDefineStaticFunc(
&MODULE_INPUT_PROTO, "wasDown", moduleInputWasDown
);
scriptProtoDefineStaticFunc(
&MODULE_INPUT_PROTO, "pressed", moduleInputPressed
);
scriptProtoDefineStaticFunc(
&MODULE_INPUT_PROTO, "released", moduleInputReleased
);
scriptProtoDefineStaticFunc(
&MODULE_INPUT_PROTO, "getValue", moduleInputGetValue
);
scriptProtoDefineStaticFunc(
&MODULE_INPUT_PROTO, "axis", moduleInputAxis
);
scriptProtoDefineStaticFunc(
&MODULE_INPUT_PROTO, "bind", moduleInputBind
);
/* Register INPUT_ACTION_* integer constants into the global scope. */
jerry_value_t result = jerry_eval(
(const jerry_char_t *)INPUT_ACTION_SCRIPT,
strlen(INPUT_ACTION_SCRIPT),
JERRY_PARSE_NO_OPTS
);
jerry_value_free(result);
}
static void moduleInputDispose(void) {
scriptProtoDispose(&MODULE_INPUT_PROTO);
}
+127
View File
@@ -0,0 +1,127 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "modulebase.h"
#include "util/memory.h"
jerry_object_native_info_t MODULE_BASE_NATIVE_INFO = {
.free_cb = moduleBaseFreeProto,
.number_of_references = 0,
.offset_of_references = 0
};
void moduleBaseFreeProto(void *ptr, jerry_object_native_info_t *info) {
assertNotNull(ptr, "Pointer must not be null");
assertNotNull(info, "Native info must not be null");
memoryFree(ptr);
}
jerry_value_t moduleBaseThrow(const char_t *message) {
assertStrLenMin(message, 1, "Error message must not be empty");
return jerry_throw_sz(JERRY_ERROR_TYPE, message);
}
jerry_value_t moduleBaseThrowError(const errorret_t err) {
assertNotNull(err.state, "Error state must not be NULL");
assertNotNull(err.state->message, "Error message must not be NULL");
jerry_value_t jsErr = jerry_throw_sz(JERRY_ERROR_TYPE, err.state->message);
errorCatch(err);
return jsErr;
}
void moduleBaseToString(jerry_value_t val, char_t *buf, jerry_size_t buflen) {
jerry_size_t len = jerry_string_to_buffer(
val, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, buflen - 1
);
buf[len] = '\0';
}
void moduleBaseExceptionMessage(
jerry_value_t exception,
char_t *buf,
size_t buflen
) {
jerry_value_t errVal = jerry_exception_value(exception, false);
jerry_value_t errStr = jerry_value_to_string(errVal);
jerry_size_t len = jerry_string_to_buffer(
errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, (jerry_size_t)(buflen - 1)
);
buf[len] = '\0';
jerry_value_free(errStr);
jerry_value_free(errVal);
}
void moduleBaseDefineMethod(
jerry_value_t obj,
const char_t *name,
jerry_external_handler_t fn
) {
jerry_value_t key = jerry_string_sz(name);
jerry_value_t func = jerry_function_external(fn);
jerry_object_set(obj, key, func);
jerry_value_free(func);
jerry_value_free(key);
}
void moduleBaseDefineGlobalMethod(
const char_t *name,
jerry_external_handler_t fn
) {
jerry_value_t global = jerry_current_realm();
moduleBaseDefineMethod(global, name, fn);
jerry_value_free(global);
}
void moduleBaseSetValue(const char_t *name, jerry_value_t value) {
jerry_value_t global = jerry_current_realm();
jerry_value_t key = jerry_string_sz(name);
jerry_object_set(global, key, value);
jerry_value_free(key);
jerry_value_free(global);
}
jerry_value_t moduleBaseWrapPointer(void *ptr) {
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MODULE_BASE_NATIVE_INFO, ptr);
return obj;
}
void *moduleBaseUnwrapPointer(jerry_value_t val) {
if(!jerry_value_is_object(val)) return NULL;
return jerry_object_get_native_ptr(val, &MODULE_BASE_NATIVE_INFO);
}
jerry_value_t moduleBaseGetProp(
jerry_value_t obj, const char_t *name
) {
jerry_value_t key = jerry_string_sz(name);
jerry_value_t val = jerry_object_get(obj, key);
jerry_value_free(key);
return val;
}
float_t moduleBaseValueFloat(jerry_value_t val) {
return (float_t)jerry_value_as_number(val);
}
int32_t moduleBaseValueInt(jerry_value_t val) {
return (int32_t)jerry_value_as_number(val);
}
void moduleBaseSetNumber(const char_t *name, double value) {
jerry_value_t global = jerry_current_realm();
jerry_value_t key = jerry_string_sz(name);
jerry_value_t val = jerry_number(value);
jerry_object_set(global, key, val);
jerry_value_free(val);
jerry_value_free(key);
jerry_value_free(global);
}
void moduleBaseSetInt(const char_t *name, int32_t value) {
moduleBaseSetNumber(name, (double)value);
}
+190
View File
@@ -0,0 +1,190 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "assert/assert.h"
#include "util/string.h"
#include <jerryscript.h>
#include <stdlib.h>
extern jerry_object_native_info_t MODULE_BASE_NATIVE_INFO;
/**
* Frees a native pointer associated with a JavaScript object, uses the internal
* memory functions.
*
* @param ptr The native pointer to free. Must not be NULL.
* @param info The native info associated with the pointer. Must not be NULL.
*/
void moduleBaseFreeProto(void *ptr, jerry_object_native_info_t *info);
/**
* Throws a JavaScript error with the given message.
*
* @param message The error message to throw. Must not be empty.
* @return A jerry_value_t representing the thrown error.
*/
jerry_value_t moduleBaseThrow(const char_t *message);
/**
* "Converts" a dusk error to a jerry type error.
*
* @param err Native dusk error to convert.
* @return A jerry_value_t representing the thrown error.
*/
jerry_value_t moduleBaseThrowError(const errorret_t err);
/**
* Converts a jerry value to a string and writes it to the provided buffer.
*
* @param val The jerry value to convert.
* @param buf The buffer to write the string to.
* @param buflen The length of the buffer.
*/
void moduleBaseToString(jerry_value_t val, char_t *buf, jerry_size_t buflen);
/**
* Converts a jerry exception value to a string and writes it to the provided
* buffer.
*
* @param exception The jerry exception value to convert.
* @param buf The buffer to write the string to.
* @param buflen The length of the buffer.
*/
void moduleBaseExceptionMessage(
jerry_value_t exception,
char_t *buf,
size_t buflen
);
/**
* Defines a method on a given object.
*
* @param obj The object to define the method on.
* @param name The name of the method.
* @param fn The function pointer to the method implementation.
*/
void moduleBaseDefineMethod(
jerry_value_t obj,
const char_t *name,
jerry_external_handler_t fn
);
/**
* Defines a global method.
*
* @param name The name of the method.
* @param fn The function pointer to the method implementation.
*/
void moduleBaseDefineGlobalMethod(
const char_t *name,
jerry_external_handler_t fn
);
/**
* Sets a global variable to a given jerry value.
*
* @param name The name of the global variable.
* @param value The jerry value to set the variable to.
*/
void moduleBaseSetValue(const char_t *name, jerry_value_t value);
/**
* Wraps a native pointer in a jerry object.
*
* @param ptr The native pointer to wrap.
* @return A jerry_value_t representing the wrapped pointer.
*/
jerry_value_t moduleBaseWrapPointer(void *ptr);
/**
* Unwraps a native pointer from a jerry object.
*
* @param val The jerry value to unwrap the pointer from.
* @return The unwrapped native pointer.
*/
void *moduleBaseUnwrapPointer(jerry_value_t val);
/**
* Gets a property from a jerry object as a jerry value.
*
* @param obj The object to get the property from.
* @param name The name of the property to get.
* @return The value of the property.
*/
jerry_value_t moduleBaseGetProp(
jerry_value_t obj, const char_t *name
);
/**
* Converts a jerry value to a float.
*
* @param val The jerry value to convert.
* @return The converted float value.
*/
float_t moduleBaseValueFloat(jerry_value_t val);
/**
* Converts a jerry value to an int.
*
* @param val The jerry value to convert.
* @return The converted int value.
*/
int32_t moduleBaseValueInt(jerry_value_t val);
/**
* Sets a global variable to a given number value.
*
* @param name The name of the global variable.
* @param value The number value to set the variable to.
*/
void moduleBaseSetNumber(const char_t *name, double value);
/**
* Sets a global variable to a given int value.
*
* @param name The name of the global variable.
* @param value The int value to set the variable to.
*/
void moduleBaseSetInt(const char_t *name, int32_t value);
#define moduleBaseFunction(name) \
static jerry_value_t name( \
const jerry_call_info_t *callInfo, \
const jerry_value_t args[], \
const jerry_length_t argc)
#define moduleBaseRequireArgs(n) do { \
if(argc < (jerry_length_t)(n)) { \
return moduleBaseThrow("Expected at least " #n " argument(s)"); \
} \
} while(0)
#define moduleBaseRequireNumber(i) do { \
if(!jerry_value_is_number(args[(i)])) { \
return moduleBaseThrow("Expected number argument"); \
} \
} while(0)
#define moduleBaseRequireString(i) do { \
if(!jerry_value_is_string(args[(i)])) { \
return moduleBaseThrow("Expected string argument"); \
} \
} while(0)
#define moduleBaseArgFloat(i) ((float_t)jerry_value_as_number(args[(i)]))
#define moduleBaseArgInt(i) ((int32_t)jerry_value_as_number(args[(i)]))
#define moduleBaseArgBool(i) (jerry_value_is_true(args[(i)]))
#define moduleBaseOptFloat(i, def) \
((jerry_length_t)(i) < argc && jerry_value_is_number(args[(i)]) \
? (float_t)jerry_value_as_number(args[(i)]) : (def))
#define moduleBaseOptInt(i, def) \
((jerry_length_t)(i) < argc && jerry_value_is_number(args[(i)]) \
? (int32_t)jerry_value_as_number(args[(i)]) : (def))
+26
View File
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/module/console/moduleconsole.h"
#include "script/module/engine/moduleengine.h"
#include "script/module/input/moduleinput.h"
#include "script/module/system/modulesystem.h"
static void moduleListInit(void) {
moduleConsoleInit();
moduleEngineInit();
moduleInputInit();
moduleSystemInit();
}
static void moduleListDispose(void) {
moduleSystemDispose();
moduleInputDispose();
moduleEngineDispose();
moduleConsoleDispose();
}
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/module/modulebase.h"
#include "script/scriptproto.h"
#include "system/system.h"
static scriptproto_t MODULE_SYSTEM_PROTO;
moduleBaseFunction(moduleSystemGetPlatform) {
return jerry_number((double)systemGetPlatform());
}
static void moduleSystemInit(void) {
scriptProtoInit(&MODULE_SYSTEM_PROTO, "System", sizeof(uint8_t), NULL);
scriptProtoDefineStaticProp(
&MODULE_SYSTEM_PROTO, "platform",
moduleSystemGetPlatform, NULL
);
/* Register PLATFORM_* integer constants on the System global. */
jerry_value_t target = MODULE_SYSTEM_PROTO.prototype;
#define SYSTEM_PLATFORM(name, value) \
do { \
jerry_value_t key = jerry_string_sz("PLATFORM_" #name); \
jerry_value_t val = jerry_number((double)(value)); \
jerry_object_set(target, key, val); \
jerry_value_free(val); \
jerry_value_free(key); \
} while(0);
SYSTEM_PLATFORM_LIST
#undef SYSTEM_PLATFORM
}
static void moduleSystemDispose(void) {
scriptProtoDispose(&MODULE_SYSTEM_PROTO);
}
+74
View File
@@ -0,0 +1,74 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "script.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "asset/asset.h"
#include "asset/loader/assetloader.h"
#include "asset/loader/assetentry.h"
#include "script/module/modulelist.h"
#include <jerryscript.h>
script_t SCRIPT;
errorret_t scriptInit(void) {
memoryZero(&SCRIPT, sizeof(script_t));
jerry_init(JERRY_INIT_EMPTY);
SCRIPT.initialized = true;
moduleListInit();
errorOk();
}
errorret_t scriptExecString(const char_t *source) {
assertNotNull(source, "Source cannot be NULL");
assertTrue(SCRIPT.initialized, "Script system not initialized");
jerry_value_t result = jerry_eval(
(const jerry_char_t *)source,
strlen(source),
JERRY_PARSE_NO_OPTS
);
if(jerry_value_is_exception(result)) {
char_t buf[256];
jerry_value_t errVal = jerry_exception_value(result, false);
jerry_value_t errStr = jerry_value_to_string(errVal);
jerry_size_t len = jerry_string_to_buffer(
errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, sizeof(buf) - 1
);
buf[len] = '\0';
jerry_value_free(errStr);
jerry_value_free(errVal);
jerry_value_free(result);
errorThrow("Script error: %s", buf);
}
jerry_value_free(result);
errorOk();
}
errorret_t scriptExecFile(const char_t *path) {
assertNotNull(path, "Path cannot be NULL");
assertTrue(SCRIPT.initialized, "Script system not initialized");
assetentry_t *entry = assetGetEntry(path, ASSET_LOADER_TYPE_SCRIPT, NULL);
assertNotNull(entry, "Failed to get asset entry for script");
errorChain(assetRequireLoaded(entry));
errorOk();
}
errorret_t scriptDispose(void) {
if(!SCRIPT.initialized) errorOk();
moduleListDispose();
jerry_cleanup();
SCRIPT.initialized = false;
errorOk();
}
+47
View File
@@ -0,0 +1,47 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
typedef struct {
bool_t initialized;
} script_t;
extern script_t SCRIPT;
/**
* Initializes the script system, starting up JerryScript and registering all
* built-in modules.
*
* @return Any error that occurred.
*/
errorret_t scriptInit(void);
/**
* Evaluates a JS source string in the global scope.
*
* @param source Null-terminated JS source to evaluate.
* @return Any error that occurred.
*/
errorret_t scriptExecString(const char_t *source);
/**
* Loads and evaluates a script asset from the archive. The result is cached
* by the asset system; repeated calls with the same path do not re-execute.
*
* @param path Path of the script inside the asset archive.
* @return Any error that occurred.
*/
errorret_t scriptExecFile(const char_t *path);
/**
* Disposes of the script system and shuts down JerryScript.
*
* @return Any error that occurred.
*/
errorret_t scriptDispose(void);
+186
View File
@@ -0,0 +1,186 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "scriptproto.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "script/module/modulebase.h"
void scriptProtoInit(
scriptproto_t *proto,
const char_t *name,
const size_t size,
jerry_external_handler_t constructor
) {
assertNotNull(proto, "Script prototype struct must not be null");
memoryZero(proto, sizeof(scriptproto_t));
proto->info = (jerry_object_native_info_t){
.free_cb = moduleBaseFreeProto,
.number_of_references = 0,
.offset_of_references = 0
};
proto->prototype = jerry_object();
proto->size = size;
if(constructor != NULL) {
proto->constructor = jerry_function_external(constructor);
jerry_value_t protoKey = jerry_string_sz("prototype");
jerry_object_set(proto->constructor, protoKey, proto->prototype);
jerry_value_free(protoKey);
jerry_value_t ctorKey = jerry_string_sz("constructor");
jerry_object_set(proto->prototype, ctorKey, proto->constructor);
jerry_value_free(ctorKey);
}
if(name != NULL) {
jerry_value_t global = jerry_current_realm();
jerry_value_t key = jerry_string_sz(name);
jerry_value_t val = proto->constructor ? proto->constructor : proto->prototype;
jerry_object_set(global, key, val);
jerry_value_free(key);
jerry_value_free(global);
}
}
void scriptProtoDefineProp(
scriptproto_t *proto,
const char_t *name,
jerry_external_handler_t getter,
jerry_external_handler_t setter
) {
assertNotNull(proto, "Script prototype struct must not be null");
assertStrLenMin(name, 1, "Property name must not be empty");
assertNotNull(getter, "Getter must not be null");
jerry_property_descriptor_t desc;
memoryZero(&desc, sizeof(desc));
desc.flags = (uint16_t)(
JERRY_PROP_IS_GET_DEFINED |
JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_ENUMERABLE |
JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE
);
desc.getter = jerry_function_external(getter);
if(setter != NULL) {
desc.flags |= JERRY_PROP_IS_SET_DEFINED;
desc.setter = jerry_function_external(setter);
}
jerry_value_t key = jerry_string_sz(name);
jerry_value_t result = jerry_object_define_own_prop(proto->prototype, key, &desc);
jerry_value_free(result);
jerry_value_free(key);
jerry_value_free(desc.getter);
if(setter != NULL) jerry_value_free(desc.setter);
}
void scriptProtoDefineFunc(
scriptproto_t *proto,
const char_t *name,
jerry_external_handler_t fn
) {
assertNotNull(proto, "Script prototype struct must not be null");
assertStrLenMin(name, 1, "Method name must not be empty");
assertNotNull(fn, "Function handler must not be null");
jerry_value_t key = jerry_string_sz(name);
jerry_value_t func = jerry_function_external(fn);
jerry_object_set(proto->prototype, key, func);
jerry_value_free(func);
jerry_value_free(key);
}
void scriptProtoDefineStaticProp(
scriptproto_t *proto,
const char_t *name,
jerry_external_handler_t getter,
jerry_external_handler_t setter
) {
assertNotNull(proto, "Script prototype struct must not be null");
assertStrLenMin(name, 1, "Property name must not be empty");
assertNotNull(getter, "Getter must not be null");
jerry_value_t target = (
proto->constructor ? proto->constructor : proto->prototype
);
jerry_property_descriptor_t desc;
memoryZero(&desc, sizeof(desc));
desc.flags = (uint16_t)(
JERRY_PROP_IS_GET_DEFINED |
JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_ENUMERABLE |
JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE
);
desc.getter = jerry_function_external(getter);
if(setter != NULL) {
desc.flags |= JERRY_PROP_IS_SET_DEFINED;
desc.setter = jerry_function_external(setter);
}
jerry_value_t key = jerry_string_sz(name);
jerry_value_t result = jerry_object_define_own_prop(target, key, &desc);
jerry_value_free(result);
jerry_value_free(key);
jerry_value_free(desc.getter);
if(setter != NULL) jerry_value_free(desc.setter);
}
void scriptProtoDefineStaticFunc(
scriptproto_t *proto,
const char_t *name,
jerry_external_handler_t fn
) {
assertNotNull(proto, "Script prototype struct must not be null");
assertStrLenMin(name, 1, "Method name must not be empty");
assertNotNull(fn, "Function handler must not be null");
jerry_value_t target = (
proto->constructor ? proto->constructor : proto->prototype
);
jerry_value_t key = jerry_string_sz(name);
jerry_value_t func = jerry_function_external(fn);
jerry_object_set(target, key, func);
jerry_value_free(func);
jerry_value_free(key);
}
jerry_value_t scriptProtoCreateValue(
const scriptproto_t *proto,
const void *value
) {
assertNotNull(proto, "Script prototype struct must not be null");
assertNotNull(value, "Value pointer must not be null");
void *ptr = memoryAllocate(proto->size);
memoryCopy(ptr, value, proto->size);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &proto->info, ptr);
jerry_object_set_proto(obj, proto->prototype);
return obj;
}
void *scriptProtoGetValue(
const scriptproto_t *proto,
const jerry_value_t obj
) {
assertNotNull(proto, "Script prototype struct must not be null");
if(!jerry_value_is_object(obj)) return NULL;
return jerry_object_get_native_ptr(obj, &proto->info);
}
void scriptProtoDefineToString(
scriptproto_t *proto,
jerry_external_handler_t fn
) {
scriptProtoDefineFunc(proto, "toString", fn);
}
void scriptProtoDispose(scriptproto_t *proto) {
assertNotNull(proto, "Script prototype struct must not be null");
jerry_value_free(proto->prototype);
if(proto->constructor) jerry_value_free(proto->constructor);
}
+156
View File
@@ -0,0 +1,156 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include <jerryscript.h>
/**
* Represents a JavaScript class prototype backed by a C struct.
* Use scriptProtoInit() to populate, scriptProtoDispose() to clean up.
*/
typedef struct {
/** Native-info tag used to identify instances of this class. */
jerry_object_native_info_t info;
/** The JS prototype object shared by all instances. */
jerry_value_t prototype;
/** The JS constructor function, or 0 if no constructor was given. */
jerry_value_t constructor;
/** sizeof the C struct wrapped by each instance. */
size_t size;
} scriptproto_t;
/**
* Initializes a JS class prototype.
*
* If name is non-NULL the class is registered as a global. When ctor is
* also non-NULL the global is the constructor function (enabling
* `new Name(...)`); otherwise the prototype object itself becomes the
* global.
*
* @param proto The struct to initialize.
* @param name JS global name, or NULL to skip global registration.
* @param size sizeof the C struct this class wraps.
* @param ctor Constructor handler, or NULL for no constructor.
*/
void scriptProtoInit(
scriptproto_t *proto,
const char_t *name,
const size_t size,
jerry_external_handler_t ctor
);
/**
* Defines an instance property with a getter and optional setter.
*
* @param proto The class prototype.
* @param name Property name.
* @param getter Getter handler (must not be NULL).
* @param setter Setter handler, or NULL for a read-only property.
*/
void scriptProtoDefineProp(
scriptproto_t *proto,
const char_t *name,
jerry_external_handler_t getter,
jerry_external_handler_t setter
);
/**
* Defines an instance method on the class prototype.
*
* @param proto The class prototype.
* @param name Method name.
* @param fn C handler called when the method is invoked.
*/
void scriptProtoDefineFunc(
scriptproto_t *proto,
const char_t *name,
jerry_external_handler_t fn
);
/**
* Defines a static property on the class (e.g. Console.visible).
*
* Attaches to the constructor when one exists, otherwise attaches
* directly to the prototype object (which is the global in that case).
*
* @param proto The class prototype.
* @param name Property name.
* @param getter Getter handler (must not be NULL).
* @param setter Setter handler, or NULL for a read-only property.
*/
void scriptProtoDefineStaticProp(
scriptproto_t *proto,
const char_t *name,
jerry_external_handler_t getter,
jerry_external_handler_t setter
);
/**
* Defines a static method on the class (e.g. Console.print).
*
* Attaches to the constructor when one exists, otherwise attaches
* directly to the prototype object.
*
* @param proto The class prototype.
* @param name Method name.
* @param fn C handler called when the static method is invoked.
*/
void scriptProtoDefineStaticFunc(
scriptproto_t *proto,
const char_t *name,
jerry_external_handler_t fn
);
/**
* Creates a JS instance wrapping a copy of a C value.
*
* Allocates a copy of the value, sets it as native data on a new JS
* object, and applies the class prototype.
*
* @param proto The class prototype.
* @param value Pointer to the C value to copy into the new JS object.
* @return A new JS object with the class prototype and native data set.
*/
jerry_value_t scriptProtoCreateValue(
const scriptproto_t *proto,
const void *value
);
/**
* Unwraps the native C pointer from a JS object instance.
*
* @param proto The class prototype.
* @param obj The JS object to inspect.
* @return Pointer to the wrapped C value, or NULL if not an instance.
*/
void *scriptProtoGetValue(
const scriptproto_t *proto,
const jerry_value_t obj
);
/**
* Defines a toString() method on the class prototype.
*
* @param proto The class prototype.
* @param fn C handler called when toString() is invoked on an
* instance.
*/
void scriptProtoDefineToString(
scriptproto_t *proto,
jerry_external_handler_t fn
);
/**
* Releases all JerryScript resources held by the prototype.
*
* Must be called before jerry_cleanup() to avoid heap finalizer
* crashes from live jerry_value_t GC roots.
*
* @param proto The class prototype to dispose.
*/
void scriptProtoDispose(scriptproto_t *proto);
+14
View File
@@ -22,4 +22,18 @@ errorret_t systemInit() {
systemdialogtype_t systemGetActiveDialogType() {
return systemGetActiveDialogTypePlatform();
}
systemplatform_t systemGetPlatform(void) {
#if defined(DUSK_KNULLI)
return SYSTEM_PLATFORM_KNULLI;
#elif defined(DUSK_PSP)
return SYSTEM_PLATFORM_PSP;
#elif defined(DUSK_GAMECUBE)
return SYSTEM_PLATFORM_GAMECUBE;
#elif defined(DUSK_WII)
return SYSTEM_PLATFORM_WII;
#else
return SYSTEM_PLATFORM_LINUX;
#endif
}
+13 -1
View File
@@ -7,6 +7,11 @@
#pragma once
#include "error/error.h"
#include "system/systemplatformlist.h"
#define SYSTEM_PLATFORM(name, value) SYSTEM_PLATFORM_##name = value,
typedef enum { SYSTEM_PLATFORM_LIST } systemplatform_t;
#undef SYSTEM_PLATFORM
typedef enum {
SYSTEM_DIALOG_TYPE_NONE,
@@ -35,4 +40,11 @@ errorret_t systemInit(void);
*
* @return Dialog type currently open.
*/
systemdialogtype_t systemGetActiveDialogType();
systemdialogtype_t systemGetActiveDialogType();
/**
* Returns the platform the engine is currently running on.
*
* @return The current platform.
*/
systemplatform_t systemGetPlatform(void);
+23
View File
@@ -0,0 +1,23 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
/**
* X-macro list of all supported platforms.
* Usage: SYSTEM_PLATFORM(name, value)
*
* Example:
* #define SYSTEM_PLATFORM(name, value) \
* case SYSTEM_PLATFORM_##name: return #name;
* SYSTEM_PLATFORM_LIST
* #undef SYSTEM_PLATFORM
*/
#define SYSTEM_PLATFORM_LIST \
SYSTEM_PLATFORM(LINUX, 0) \
SYSTEM_PLATFORM(KNULLI, 1) \
SYSTEM_PLATFORM(PSP, 2) \
SYSTEM_PLATFORM(GAMECUBE, 3) \
SYSTEM_PLATFORM(WII, 4)