More cleanup

This commit is contained in:
2026-04-29 22:39:47 -05:00
parent 61f69af35a
commit ffed626447
14 changed files with 473 additions and 247 deletions
+18 -31
View File
@@ -1,35 +1,22 @@
var Cube = { function CubeEntity() {
create: function() { Entity.call(this);
var e = Entity.create(); this.add(POSITION);
e.add(COMPONENT_TYPE_POSITION); this.position.x = 0;
e.position.x = 0; this.position.y = 0;
e.position.y = 0; this.position.z = 0;
e.position.z = 0; this.add(MESH);
e.add(COMPONENT_TYPE_MESH); this.add(MATERIAL);
e.add(COMPONENT_TYPE_MATERIAL); }
// e.material.setColor(Color.black());
print(Color); Object.assign(CubeEntity.prototype, Entity.prototype);
print(Color.prototype);
return { CubeEntity.prototype.update = function() {
_e: e, var speed = 3.0;
update: Cube.update, var dx = inputAxis(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT);
dispose: Cube.dispose var dz = inputAxis(INPUT_ACTION_UP, INPUT_ACTION_DOWN);
}; this.position.x += dx * speed * TIME.delta;
}, this.position.z += dz * speed * TIME.delta;
this.material.setColor(Color.rainbow());
update: function() {
var speed = 3.0;
var dx = inputAxis(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT);
var dz = inputAxis(INPUT_ACTION_UP, INPUT_ACTION_DOWN);
this._e.position.x += dx * speed * TIME.delta;
this._e.position.z += dz * speed * TIME.delta;
},
dispose: function() {
this._e.dispose();
}
}; };
Cube; module = CubeEntity;
+19 -21
View File
@@ -1,28 +1,26 @@
var Cube = include('entities/cube.js'); var Cube = include('entities/cube.js');
var cam; function CubeScene() {
var cube; this.cam = new Entity();
this.cam.add(POSITION);
this.cam.position.x = 3;
this.cam.position.y = 3;
this.cam.position.z = 3;
this.cam.position.lookAt(0, 0, 0);
this.cam.add(CAMERA);
var SceneCube = { this.cube = new Cube();
init: function() { }
cam = Entity.create();
cam.add(COMPONENT_TYPE_POSITION);
cam.position.x = 3;
cam.position.y = 3;
cam.position.z = 3;
cam.position.lookAt(0, 0, 0);
cam.add(COMPONENT_TYPE_CAMERA);
cube = Cube.create();
},
update: function() { Object.assign(CubeScene, Scene.prototype);
cube.update();
},
dispose: function() { CubeScene.prototype.update = function() {
cam.dispose(); this.cube.update();
cube.dispose();
}
}; };
SceneCube; CubeScene.prototype.dispose = function() {
this.cam.dispose();
this.cube.dispose();
};
module = CubeScene;
+1 -1
View File
@@ -13,7 +13,7 @@ componentdefinition_t COMPONENT_DEFINITIONS[] = {
[COMPONENT_TYPE_NULL] = { 0 }, [COMPONENT_TYPE_NULL] = { 0 },
#define X(enumName, type, field, iMethod, dMethod) \ #define X(enumName, type, field, iMethod, dMethod) \
[COMPONENT_TYPE_##enumName] = { .init = iMethod, .dispose = dMethod }, [COMPONENT_TYPE_##enumName] = { .name = #field, .init = iMethod, .dispose = dMethod },
#include "componentlist.h" #include "componentlist.h"
#undef X #undef X
+1
View File
@@ -20,6 +20,7 @@ typedef union {
} componentdata_t; } componentdata_t;
typedef struct { typedef struct {
const char_t *name;
void (*init)(const entityid_t, const componentid_t); void (*init)(const entityid_t, const componentid_t);
void (*dispose)(const entityid_t, const componentid_t); void (*dispose)(const entityid_t, const componentid_t);
} componentdefinition_t; } componentdefinition_t;
+15 -7
View File
@@ -167,19 +167,27 @@ errorret_t sceneSetImmediate(const char_t *scene) {
); );
if(scene != NULL) { if(scene != NULL) {
jerry_value_t sceneObj = SCENE_SCRIPT_REF_NONE; jerry_value_t sceneClass = SCENE_SCRIPT_REF_NONE;
errorChain(scriptContextExecFile( errorChain(scriptContextExecFile(
&SCRIPT_MANAGER.mainContext, scene, &sceneObj &SCRIPT_MANAGER.mainContext, scene, &sceneClass
)); ));
if(!jerry_value_is_object(sceneObj)) { if(!jerry_value_is_function(sceneClass)) {
if(sceneObj != SCENE_SCRIPT_REF_NONE) jerry_value_free(sceneObj); if(sceneClass != SCENE_SCRIPT_REF_NONE) jerry_value_free(sceneClass);
errorThrow("Scene '%s' must return an object", scene); errorThrow("Scene '%s' must export a constructor function", scene);
}
jerry_value_t sceneObj = jerry_construct(sceneClass, NULL, 0);
jerry_value_free(sceneClass);
if(jerry_value_is_exception(sceneObj)) {
char_t errMsg[512];
moduleBaseExceptionMessage(sceneObj, errMsg, sizeof(errMsg));
jerry_value_free(sceneObj);
errorThrow("Scene '%s' constructor threw: %s", scene, errMsg);
} }
SCENE.scriptRef = sceneObj; SCENE.scriptRef = sceneObj;
errorChain(moduleSceneCall("init"));
SCENE.sceneActive = true; SCENE.sceneActive = true;
} }
+25 -11
View File
@@ -12,7 +12,7 @@
#include "script/scriptproto.h" #include "script/scriptproto.h"
// Define the prototype. // Define the prototype.
scriptproto_t MODULE_COLOR_PROTO; static scriptproto_t MODULE_COLOR_PROTO;
// Getters // Getters
moduleBaseFunction(moduleColorGetR) { moduleBaseFunction(moduleColorGetR) {
@@ -88,12 +88,25 @@ moduleBaseFunction(moduleColorSetA) {
return jerry_undefined(); return jerry_undefined();
} }
moduleBaseFunction(moduleColorToString) {
color_t *color = (color_t*)scriptProtoGetValue(
&MODULE_COLOR_PROTO, callInfo->this_value
);
if(!color) return jerry_undefined();
char_t buf[32];
stringFormat(buf, sizeof(buf), "[ %d, %d, %d, %d ]",
(int32_t)color->r, (int32_t)color->g,
(int32_t)color->b, (int32_t)color->a
);
return jerry_string_sz(buf);
}
// Constructor // Constructor
static jerry_value_t moduleColorMakeObject(color_t color) { static jerry_value_t moduleColorMakeObject(color_t color) {
return scriptProtoCreateValue(&MODULE_COLOR_PROTO, &color); return scriptProtoCreateValue(&MODULE_COLOR_PROTO, &color);
} }
moduleBaseFunction(moduleColorRGBA) { moduleBaseFunction(moduleColorConstructor) {
color_t c; color_t c;
if(argc > 0) { if(argc > 0) {
@@ -158,21 +171,22 @@ moduleBaseFunction(moduleColorRainbow) {
// Root Module // Root Module
static void moduleColor(void) { static void moduleColor(void) {
scriptProtoInit(&MODULE_COLOR_PROTO, sizeof(color_t)); scriptProtoInit(
&MODULE_COLOR_PROTO,
"Color",
sizeof(color_t),
moduleColorConstructor
);
#define X(x, g, s) \ #define X(x, g, s) \
scriptProtoDefineProperty(&MODULE_COLOR_PROTO, #x, g, s); scriptProtoDefineProp(&MODULE_COLOR_PROTO, #x, g, s);
X(r, moduleColorGetR, moduleColorSetR); X(r, moduleColorGetR, moduleColorSetR);
X(g, moduleColorGetG, moduleColorSetG); X(g, moduleColorGetG, moduleColorSetG);
X(b, moduleColorGetB, moduleColorSetB); X(b, moduleColorGetB, moduleColorSetB);
X(a, moduleColorGetA, moduleColorSetA); X(a, moduleColorGetA, moduleColorSetA);
#undef X #undef X
scriptProtoDefineMethod(&MODULE_COLOR_PROTO, "rgba", moduleColorRGBA); scriptProtoDefineStaticFunc(&MODULE_COLOR_PROTO, "rainbow", moduleColorRainbow);
scriptProtoDefineMethod( scriptProtoDefineToString(&MODULE_COLOR_PROTO, moduleColorToString);
&MODULE_COLOR_PROTO,"rainbow", moduleColorRainbow moduleBaseEval(COLOR_SCRIPT);
);
scriptProtoCreateGlobal(&MODULE_COLOR_PROTO, "Color");
moduleBaseEval("Color.prototype.black = function() { print('test'); };");
} }
@@ -45,21 +45,14 @@ moduleBaseFunction(moduleScreenSetBackground) {
} }
static void moduleScreen(void) { static void moduleScreen(void) {
// Define the prototype scriptProtoInit(&MODULE_SCREEN_PROTO, "Screen", sizeof(screen_t), NULL);
scriptProtoInit(&MODULE_SCREEN_PROTO, sizeof(screen_t));
#define X(x, g, s) \ #define X(x, g, s) \
scriptProtoDefineProperty(&MODULE_SCREEN_PROTO, #x, g, s); scriptProtoDefineProp(&MODULE_SCREEN_PROTO, #x, g, s);
// Setup the getters and setters
X(width, moduleScreenGetWidth, NULL) X(width, moduleScreenGetWidth, NULL)
X(height, moduleScreenGetHeight, NULL) X(height, moduleScreenGetHeight, NULL)
X(aspect, moduleScreenGetAspect, NULL) X(aspect, moduleScreenGetAspect, NULL)
X(background, moduleScreenGetBackground, moduleScreenSetBackground) X(background, moduleScreenGetBackground, moduleScreenSetBackground)
#undef X #undef X
// Create global Screen object
scriptProtoCreateGlobal(&MODULE_SCREEN_PROTO, "Screen");
} }
+100 -66
View File
@@ -7,81 +7,89 @@
#pragma once #pragma once
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "script/scriptproto.h"
#include "entity/entity.h" #include "entity/entity.h"
#include "entity/entitymanager.h" #include "entity/entitymanager.h"
#include "component/moduleentityposition.h" #include "component/moduleentityposition.h"
#include "component/moduleentitycamera.h" #include "component/moduleentitycamera.h"
#include "component/moduleentitymesh.h" #include "component/moduleentitymesh.h"
#include "component/moduleentitymaterial.h" #include "component/moduleentitymaterial.h"
#include "component/moduleentityphysics.h" #include "component/moduleentityphysics.h"
// ---- component type constants script ---- typedef struct {
entityid_t id;
} entityscript_t;
#define X(enumName, type, field, init, dispose) \ static scriptproto_t MODULE_ENTITY_PROTO;
"var COMPONENT_TYPE_" #enumName " = \"" #field "\";\n"
static const char_t *COMPONENT_TYPE_SCRIPT =
#include "entity/componentlist.h"
;
#undef X
// ---- Entity base class script ---- moduleBaseFunction(moduleEntityConstructor) {
entityscript_t *inst = (entityscript_t*)memoryAllocate(sizeof(entityscript_t));
static const char_t *ENTITY_SCRIPT = inst->id = entityManagerAdd();
"var Entity = {};\n" jerry_object_set_native_ptr(callInfo->this_value, &MODULE_ENTITY_PROTO.info, inst);
"Entity.POSITION = COMPONENT_TYPE_POSITION;\n" return jerry_undefined();
"Entity.CAMERA = COMPONENT_TYPE_CAMERA;\n" }
"Entity.MESH = COMPONENT_TYPE_MESH;\n"
"Entity.MATERIAL = COMPONENT_TYPE_MATERIAL;\n" moduleBaseFunction(moduleEntityAddComponent) {
"Entity.PHYSICS = COMPONENT_TYPE_PHYSICS;\n" entityscript_t *inst = (entityscript_t*)scriptProtoGetValue(
"\n" &MODULE_ENTITY_PROTO, callInfo->this_value
"var _addFns = {};\n" );
"_addFns[COMPONENT_TYPE_POSITION] = entityPositionAdd;\n" if(!inst) return moduleBaseThrow("Entity.add: invalid entity");
"_addFns[COMPONENT_TYPE_CAMERA] = entityCameraAdd;\n" moduleBaseRequireArgs(1);
"_addFns[COMPONENT_TYPE_MESH] = entityMeshAdd;\n"
"_addFns[COMPONENT_TYPE_MATERIAL] = entityMaterialAdd;\n" if(!jerry_value_is_object(args[0])) {
"_addFns[COMPONENT_TYPE_PHYSICS] = entityPhysicsAdd;\n" return moduleBaseThrow("Entity.add: expected a component type object");
"\n" }
"Entity.create = function() {\n"
" var self = Object.create(Entity);\n" jerry_value_t nameKey = jerry_string_sz("name");
" self.id = entityAdd();\n" jerry_value_t nameJsVal = jerry_object_get(args[0], nameKey);
" return self;\n" jerry_value_free(nameKey);
"};\n"
"\n" if(!jerry_value_is_string(nameJsVal)) {
"Entity.add = function(componentType) {\n" jerry_value_free(nameJsVal);
" var fn = _addFns[componentType];\n" return moduleBaseThrow("Entity.add: component type must have a .name property");
" if(!fn) throw new Error('unknown component type: ' + String(componentType));\n" }
" this[componentType] = fn(this.id);\n"
" return this[componentType];\n" char_t nameStr[64];
"};\n" moduleBaseToString(nameJsVal, nameStr, sizeof(nameStr));
"\n" jerry_value_free(nameJsVal);
"Entity.dispose = function() {\n"
" entityRemove(this.id);\n" jerry_external_handler_t addFn = NULL;
"};\n" const char_t *propName = NULL;
;
if(stringCompare(nameStr, "POSITION") == 0) {
// ---- module functions ---- addFn = moduleEntityPositionAdd; propName = "position";
} else if(stringCompare(nameStr, "CAMERA") == 0) {
/** addFn = moduleEntityCameraAdd; propName = "camera";
* Allocates a new entity and returns its id as a JS number. } else if(stringCompare(nameStr, "MESH") == 0) {
*/ addFn = moduleEntityMeshAdd; propName = "mesh";
moduleBaseFunction(moduleEntityAdd) { } else if(stringCompare(nameStr, "MATERIAL") == 0) {
return jerry_number((double)entityManagerAdd()); addFn = moduleEntityMaterialAdd; propName = "material";
} } else if(stringCompare(nameStr, "PHYSICS") == 0) {
addFn = moduleEntityPhysicsAdd; propName = "physics";
/** }
* Disposes the entity with the given id.
* Arg 0: entity id (number). if(!addFn) return moduleBaseThrow("Entity.add: unknown component type");
*/
moduleBaseFunction(moduleEntityRemove) { jerry_value_t idVal = jerry_number((double)inst->id);
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0); jerry_value_t handle = addFn(callInfo, &idVal, 1);
entityDispose((entityid_t)jerry_value_as_number(args[0])); jerry_value_free(idVal);
jerry_value_t propKey = jerry_string_sz(propName);
jerry_object_set(callInfo->this_value, propKey, handle);
jerry_value_free(propKey);
return handle;
}
moduleBaseFunction(moduleEntityDisposeMethod) {
entityscript_t *inst = (entityscript_t*)scriptProtoGetValue(
&MODULE_ENTITY_PROTO, callInfo->this_value
);
if(!inst) return jerry_undefined();
entityDispose(inst->id);
return jerry_undefined(); return jerry_undefined();
} }
/**
* Registers all entity and component modules, component type constants, and
* the Entity base object into the JS realm.
*/
static void moduleEntity(void) { static void moduleEntity(void) {
moduleEntityPosition(); moduleEntityPosition();
moduleEntityCamera(); moduleEntityCamera();
@@ -89,9 +97,35 @@ static void moduleEntity(void) {
moduleEntityMaterial(); moduleEntityMaterial();
moduleEntityPhysics(); moduleEntityPhysics();
moduleBaseFunctionRegister("entityAdd", moduleEntityAdd); scriptProtoInit(
moduleBaseFunctionRegister("entityRemove", moduleEntityRemove); &MODULE_ENTITY_PROTO,
"Entity",
sizeof(entityscript_t),
moduleEntityConstructor
);
moduleBaseEval(COMPONENT_TYPE_SCRIPT); scriptProtoDefineFunc(&MODULE_ENTITY_PROTO, "add", moduleEntityAddComponent);
moduleBaseEval(ENTITY_SCRIPT); scriptProtoDefineFunc(&MODULE_ENTITY_PROTO, "dispose", moduleEntityDisposeMethod);
// Register component type objects as globals and as Entity.POSITION etc.
// Each object has a .name property with the uppercase enum name, e.g. "POSITION".
jerry_value_t global = jerry_current_realm();
#define X(enumName, type, field, init, dispose) { \
jerry_value_t ctObj = jerry_object(); \
jerry_value_t nk = jerry_string_sz("name"); \
jerry_value_t nv = jerry_string_sz(#enumName); \
jerry_object_set(ctObj, nk, nv); \
jerry_value_free(nv); \
jerry_value_free(nk); \
jerry_value_t gk = jerry_string_sz(#enumName); \
jerry_object_set(global, gk, ctObj); \
jerry_value_free(gk); \
jerry_value_t ek = jerry_string_sz(#enumName); \
jerry_object_set(MODULE_ENTITY_PROTO.constructor, ek, ctObj); \
jerry_value_free(ek); \
jerry_value_free(ctObj); \
}
#include "entity/componentlist.h"
#undef X
jerry_value_free(global);
} }
+44 -9
View File
@@ -7,20 +7,40 @@
#pragma once #pragma once
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "script/scriptproto.h"
#include "scene/scene.h" #include "scene/scene.h"
static scriptproto_t MODULE_SCENE_PROTO;
// Default no-op lifecycle methods — JS scene instances override these.
moduleBaseFunction(moduleSceneDefaultUpdate) {
return jerry_undefined();
}
moduleBaseFunction(moduleSceneDefaultDispose) {
return jerry_undefined();
}
// Constructor — returns undefined so JerryScript uses `this`, which already
// has Scene.prototype set. Scenes carry no C struct; all state lives in JS.
moduleBaseFunction(moduleSceneConstructor) {
return jerry_undefined();
}
// Static: Scene.set(name)
moduleBaseFunction(moduleSceneSet) { moduleBaseFunction(moduleSceneSet) {
moduleBaseRequireArgs(1); moduleBaseRequireArgs(1);
moduleBaseRequireString(0); moduleBaseRequireString(0);
char_t name[ASSET_FILE_PATH_MAX]; char_t name[ASSET_FILE_PATH_MAX];
moduleBaseToString(args[0], name, sizeof(name)); moduleBaseToString(args[0], name, sizeof(name));
if(name[0] == '\0') return moduleBaseThrow("Scene.set: Scene name cannot be empty"); if(name[0] == '\0') return moduleBaseThrow("Scene.set: name cannot be empty");
sceneSet(name); sceneSet(name);
return jerry_undefined(); return jerry_undefined();
} }
// Static: Scene.current (getter)
moduleBaseFunction(moduleSceneGetCurrent) { moduleBaseFunction(moduleSceneGetCurrent) {
if(SCENE.sceneCurrent[0] == '\0') return jerry_undefined(); if(SCENE.sceneCurrent[0] == '\0') return jerry_undefined();
return jerry_string_sz(SCENE.sceneCurrent); return jerry_string_sz(SCENE.sceneCurrent);
@@ -32,13 +52,15 @@ static void moduleSceneReset(void) {
SCENE.scriptRef = SCENE_SCRIPT_REF_NONE; SCENE.scriptRef = SCENE_SCRIPT_REF_NONE;
} }
jerry_value_t obj = jerry_object(); // Drop the 'module' global reference to the scene class so JerryScript's
// GC can collect it and all associated closures.
moduleBaseDefineMethod(obj, "set", moduleSceneSet); jerry_value_t global = jerry_current_realm();
moduleBaseDefineProperty(obj, "current", moduleSceneGetCurrent, NULL); jerry_value_t key = jerry_string_sz("module");
jerry_value_t undef = jerry_undefined();
moduleBaseSetValue("Scene", obj); jerry_object_set(global, key, undef);
jerry_value_free(obj); jerry_value_free(undef);
jerry_value_free(key);
jerry_value_free(global);
} }
static errorret_t moduleSceneCall(const char_t *method) { static errorret_t moduleSceneCall(const char_t *method) {
@@ -72,5 +94,18 @@ static errorret_t moduleSceneCall(const char_t *method) {
} }
static void moduleScene(void) { static void moduleScene(void) {
moduleSceneReset(); scriptProtoInit(
&MODULE_SCENE_PROTO,
"Scene",
sizeof(uint8_t),
moduleSceneConstructor
);
scriptProtoDefineFunc(&MODULE_SCENE_PROTO, "update", moduleSceneDefaultUpdate);
scriptProtoDefineFunc(&MODULE_SCENE_PROTO, "dispose", moduleSceneDefaultDispose);
scriptProtoDefineStaticFunc(&MODULE_SCENE_PROTO, "set", moduleSceneSet);
scriptProtoDefineStaticProp(
&MODULE_SCENE_PROTO, "current", moduleSceneGetCurrent, NULL
);
} }
@@ -0,0 +1,59 @@
/**
* 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"
moduleBaseFunction(moduleInclude) {
if(argc < 1 || !jerry_value_is_string(args[0])) {
return moduleBaseThrow("Expected string filename");
}
char_t filename[1024];
moduleBaseToString(args[0], filename, sizeof(filename));
if(filename[0] == '\0') {
return moduleBaseThrow("Filename cannot be empty");
}
size_t len = strlen(filename);
if(len < 3 || stringCompare(&filename[len - 3], ".js") != 0) {
return moduleBaseThrow("include: filename must end with .js");
}
char_t buffer[1024];
stringCopy(buffer, filename, sizeof(buffer));
// Save and reset 'export' so the included module gets a clean undefined
// default. Saving lets nested includes each have their own export scope.
jerry_value_t global = jerry_current_realm();
jerry_value_t moduleKey = jerry_string_sz("module");
jerry_value_t prevModule = jerry_object_get(global, moduleKey);
jerry_value_t undef = jerry_undefined();
jerry_object_set(global, moduleKey, undef);
jerry_value_free(undef);
jerry_value_t result = 0;
errorret_t err = scriptContextExecFile(scriptContextCurrent, buffer, &result);
if(result != 0) jerry_value_free(result);
// Capture whatever the module assigned to 'module', then restore the
// caller's value so nested includes don't clobber each other.
jerry_value_t moduleVal = jerry_object_get(global, moduleKey);
jerry_object_set(global, moduleKey, prevModule);
jerry_value_free(prevModule);
jerry_value_free(moduleKey);
jerry_value_free(global);
if(err.code != ERROR_OK) {
jerry_value_free(moduleVal);
errorCatch(errorPrint(err));
return moduleBaseThrow("Failed to include script file");
}
return moduleVal;
}
+2 -36
View File
@@ -7,6 +7,7 @@
#pragma once #pragma once
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "script/module/script/moduleinclude.h"
#include "console/console.h" #include "console/console.h"
moduleBaseFunction(moduleScriptPrint) { moduleBaseFunction(moduleScriptPrint) {
@@ -35,42 +36,7 @@ moduleBaseFunction(moduleScriptPrint) {
return jerry_undefined(); return jerry_undefined();
} }
moduleBaseFunction(moduleScriptInclude) {
if(argc < 1 || !jerry_value_is_string(args[0])) {
return moduleBaseThrow("Expected string filename");
}
char_t filename[1024];
moduleBaseToString(args[0], filename, sizeof(filename));
if(filename[0] == '\0') {
return moduleBaseThrow("Filename cannot be empty");
}
char_t buffer[1024];
stringCopy(buffer, filename, sizeof(buffer));
size_t len = strlen(buffer);
if(len < 3 || stringCompare(&buffer[len - 3], ".js") != 0) {
if(len + 3 >= sizeof(buffer)) {
return moduleBaseThrow("Filename too long to append .js");
}
stringCopy(&buffer[len], ".js", 4);
}
jerry_value_t result = 0;
errorret_t err = scriptContextExecFile(scriptContextCurrent, buffer, &result);
if(err.code != ERROR_OK) {
if(result != 0) jerry_value_free(result);
errorCatch(errorPrint(err));
return moduleBaseThrow("Failed to include script file");
}
if(result == 0) return jerry_undefined();
return result;
}
static void moduleScript(void) { static void moduleScript(void) {
moduleBaseFunctionRegister("print", moduleScriptPrint); moduleBaseFunctionRegister("print", moduleScriptPrint);
moduleBaseFunctionRegister("include", moduleScriptInclude); moduleBaseFunctionRegister("include", moduleInclude);
} }
+94 -20
View File
@@ -1,6 +1,6 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
@@ -10,7 +10,12 @@
#include "util/memory.h" #include "util/memory.h"
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
void scriptProtoInit(scriptproto_t *proto, const size_t size) { 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"); assertNotNull(proto, "Script prototype struct must not be null");
memoryZero(proto, sizeof(scriptproto_t)); memoryZero(proto, sizeof(scriptproto_t));
@@ -19,13 +24,35 @@ void scriptProtoInit(scriptproto_t *proto, const size_t size) {
.number_of_references = 0, .number_of_references = 0,
.offset_of_references = 0 .offset_of_references = 0
}; };
proto->prototype = jerry_object(); proto->prototype = jerry_object();
proto->size = size; 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;
if(proto->constructor) {
val = proto->constructor;
} else {
val = proto->prototype;
}
jerry_object_set(global, key, val);
jerry_value_free(key);
jerry_value_free(global);
}
} }
void scriptProtoDefineProperty( void scriptProtoDefineProp(
scriptproto_t *proto, scriptproto_t *proto,
const char_t *name, const char_t *name,
jerry_external_handler_t getter, jerry_external_handler_t getter,
@@ -51,16 +78,14 @@ void scriptProtoDefineProperty(
} }
jerry_value_t key = jerry_string_sz(name); jerry_value_t key = jerry_string_sz(name);
jerry_value_t result = jerry_object_define_own_prop( jerry_value_t result = jerry_object_define_own_prop(proto->prototype, key, &desc);
proto->prototype, key, &desc
);
jerry_value_free(result); jerry_value_free(result);
jerry_value_free(key); jerry_value_free(key);
jerry_value_free(desc.getter); jerry_value_free(desc.getter);
if(setter != NULL) jerry_value_free(desc.setter); if(setter != NULL) jerry_value_free(desc.setter);
} }
void scriptProtoDefineMethod( void scriptProtoDefineFunc(
scriptproto_t *proto, scriptproto_t *proto,
const char_t *name, const char_t *name,
jerry_external_handler_t fn jerry_external_handler_t fn
@@ -76,24 +101,56 @@ void scriptProtoDefineMethod(
jerry_value_free(key); jerry_value_free(key);
} }
void scriptProtoCreateGlobal( void scriptProtoDefineStaticProp(
scriptproto_t *proto, scriptproto_t *proto,
const char_t *name const char_t *name,
jerry_external_handler_t getter,
jerry_external_handler_t setter
) { ) {
assertNotNull(proto, "Script prototype struct must not be null"); assertNotNull(proto, "Script prototype struct must not be null");
assertStrLenMin(name, 1, "Global object name must not be empty"); 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 global = jerry_current_realm();
jerry_value_t key = jerry_string_sz(name); jerry_value_t key = jerry_string_sz(name);
jerry_object_set(global, key, proto->prototype); jerry_value_t result = jerry_object_define_own_prop(target, key, &desc);
jerry_value_free(result);
jerry_value_free(key); jerry_value_free(key);
jerry_value_free(global); jerry_value_free(desc.getter);
if(setter != NULL) jerry_value_free(desc.setter);
} }
void * scriptProtoGetValue(const scriptproto_t *proto, const jerry_value_t obj){ void scriptProtoDefineStaticFunc(
scriptproto_t *proto,
const char_t *name,
jerry_external_handler_t fn
) {
assertNotNull(proto, "Script prototype struct must not be null"); assertNotNull(proto, "Script prototype struct must not be null");
if(!jerry_value_is_object(obj)) return NULL; assertStrLenMin(name, 1, "Method name must not be empty");
return jerry_object_get_native_ptr(obj, &proto->info); 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( jerry_value_t scriptProtoCreateValue(
@@ -102,16 +159,33 @@ jerry_value_t scriptProtoCreateValue(
) { ) {
assertNotNull(proto, "Script prototype struct must not be null"); assertNotNull(proto, "Script prototype struct must not be null");
assertNotNull(value, "Value pointer must not be null"); assertNotNull(value, "Value pointer must not be null");
void *ptr = memoryAllocate(proto->size); void *ptr = memoryAllocate(proto->size);
memoryCopy(ptr, value, proto->size); memoryCopy(ptr, value, proto->size);
jerry_value_t obj = jerry_object(); jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, ptr, &proto->info); jerry_object_set_native_ptr(obj, &proto->info, ptr);
jerry_object_set_proto(obj, proto->prototype); jerry_object_set_proto(obj, proto->prototype);
return obj; 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) { void scriptProtoDispose(scriptproto_t *proto) {
assertNotNull(proto, "Script prototype struct must not be null"); assertNotNull(proto, "Script prototype struct must not be null");
jerry_value_free(proto->prototype); jerry_value_free(proto->prototype);
if(proto->constructor) jerry_value_free(proto->constructor);
} }
+92 -35
View File
@@ -1,6 +1,6 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
@@ -12,26 +12,38 @@
typedef struct { typedef struct {
jerry_object_native_info_t info; jerry_object_native_info_t info;
jerry_value_t prototype; jerry_value_t prototype;
jerry_value_t constructor;
size_t size; size_t size;
} scriptproto_t; } scriptproto_t;
/** /**
* Initialize a script prototype struct. * Initialize a JS class prototype.
* *
* @param proto The script prototype struct to initialize. * If name is non-NULL the class is registered as a global. When ctor is also
* @param size Size of the struct that this proto represents. * 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 if the class has no constructor.
*/ */
void scriptProtoInit(scriptproto_t *proto, const size_t size); void scriptProtoInit(
scriptproto_t *proto,
const char_t *name,
const size_t size,
jerry_external_handler_t ctor
);
/** /**
* Define a property on a prototype with the provided getter and setter. * Define an instance property with a getter and optional setter.
* *
* @param proto The prototype to define the property on. * @param proto The class prototype.
* @param name The name of the property. * @param name Property name.
* @param getter Function to call for getting. * @param getter Getter handler (must not be NULL).
* @param setter Function to call for setting (or NULL for read-only). * @param setter Setter handler, or NULL for a read-only property.
*/ */
void scriptProtoDefineProperty( void scriptProtoDefineProp(
scriptproto_t *proto, scriptproto_t *proto,
const char_t *name, const char_t *name,
jerry_external_handler_t getter, jerry_external_handler_t getter,
@@ -39,35 +51,58 @@ void scriptProtoDefineProperty(
); );
/** /**
* Define a method on a prototype. * Define an instance method on the class prototype.
* *
* @param proto The prototype to define the method on. * @param proto The class prototype.
* @param name The name of the method. * @param name Method name.
* @param fn The C function to call when the method is invoked in JS. * @param fn C handler called when the method is invoked.
*/ */
void scriptProtoDefineMethod( void scriptProtoDefineFunc(
scriptproto_t *proto, scriptproto_t *proto,
const char_t *name, const char_t *name,
jerry_external_handler_t fn jerry_external_handler_t fn
); );
/** /**
* Defines a global object with the provided name and prototype. * Define a static property on the class (e.g. Scene.current).
* *
* @param proto The prototype struct to use for the global object. * Attaches to the constructor function when one exists, otherwise attaches
* @param name The name of the global object to create. * 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 scriptProtoCreateGlobal( void scriptProtoDefineStaticProp(
scriptproto_t *proto, scriptproto_t *proto,
const char_t *name const char_t *name,
jerry_external_handler_t getter,
jerry_external_handler_t setter
); );
/** /**
* Create a JS object based on a C value using the provided prototype. * Define a static method on the class (e.g. Color.fromRGBA).
* *
* @param proto The prototype. * Attaches to the constructor function when one exists, otherwise attaches
* @param value The pointer to the C value to create a JS object for. * directly to the prototype object (which is the global in that case).
* @return A JS object wrapping the provided C value. *
* @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
);
/**
* Create a JS instance wrapping a copy of a C value.
*
* @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 pointer set.
*/ */
jerry_value_t scriptProtoCreateValue( jerry_value_t scriptProtoCreateValue(
const scriptproto_t *proto, const scriptproto_t *proto,
@@ -75,9 +110,31 @@ jerry_value_t scriptProtoCreateValue(
); );
/** /**
* Gets the native C struct pointer from a JS object using the prototype info. * Unwrap the native C pointer from a JS object.
* *
* @param proto The prototype struct containing the native info. * @param proto The class prototype.
* @param obj The JS object to get the native value from. * @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); void *scriptProtoGetValue(
const scriptproto_t *proto,
const jerry_value_t obj
);
/**
* Define the 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
);
/**
* Release all JerryScript resources held by the prototype.
*
* @param proto The class prototype to dispose.
*/
void scriptProtoDispose(scriptproto_t *proto);
+1 -1
View File
@@ -62,7 +62,7 @@ for name, (r, g, b, a) in colors.items():
] ]
js += [ js += [
f"Color.{name.lower()} = function() {{", f"Color.{name.lower()} = function() {{",
f" return Color.rgba({r8}, {g8}, {b8}, {a8});", f" return new Color({r8}, {g8}, {b8}, {a8});",
"};", "};",
"", "",
] ]