Scripting: Add callback removing

This commit is contained in:
Vicki Pfau 2022-06-05 20:36:54 -07:00
parent 6289c1a4d1
commit 3687863cbc
5 changed files with 95 additions and 8 deletions

View File

@ -29,6 +29,8 @@ struct mScriptContext {
struct Table weakrefs;
uint32_t nextWeakref;
struct Table callbacks;
struct Table callbackId;
uint32_t nextCallbackId;
struct mScriptValue* constants;
struct Table docstrings;
};
@ -85,7 +87,8 @@ void mScriptContextExportConstants(struct mScriptContext* context, const char* n
void mScriptContextExportNamespace(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* value);
void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback);
void mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value);
uint32_t mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value);
void mScriptContextRemoveCallback(struct mScriptContext*, uint32_t cbid);
void mScriptContextSetDocstring(struct mScriptContext*, const char* key, const char* docstring);
const char* mScriptContextGetDocstring(struct mScriptContext*, const char* key);

View File

@ -14,6 +14,11 @@ struct mScriptFileInfo {
struct mScriptEngineContext* context;
};
struct mScriptCallbackInfo {
const char* callback;
size_t id;
};
static void _engineContextDestroy(void* ctx) {
struct mScriptEngineContext* context = ctx;
context->destroy(context);
@ -56,6 +61,8 @@ void mScriptContextInit(struct mScriptContext* context) {
TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref);
context->nextWeakref = 1;
HashTableInit(&context->callbacks, 0, (void (*)(void*)) mScriptValueDeref);
TableInit(&context->callbackId, 0, free);
context->nextCallbackId = 1;
context->constants = NULL;
HashTableInit(&context->docstrings, 0, NULL);
}
@ -66,6 +73,7 @@ void mScriptContextDeinit(struct mScriptContext* context) {
mScriptContextDrainPool(context);
mScriptListDeinit(&context->refPool);
HashTableDeinit(&context->callbacks);
TableDeinit(&context->callbackId);
HashTableDeinit(&context->engines);
HashTableDeinit(&context->docstrings);
}
@ -199,8 +207,11 @@ void mScriptContextTriggerCallback(struct mScriptContext* context, const char* c
size_t i;
for (i = 0; i < mScriptListSize(list->value.list); ++i) {
struct mScriptFrame frame;
mScriptFrameInit(&frame);
struct mScriptValue* fn = mScriptListGetPointer(list->value.list, i);
if (!fn->type) {
continue;
}
mScriptFrameInit(&frame);
if (fn->type->base == mSCRIPT_TYPE_WRAPPER) {
fn = mScriptValueUnwrap(fn);
}
@ -209,16 +220,48 @@ void mScriptContextTriggerCallback(struct mScriptContext* context, const char* c
}
}
void mScriptContextAddCallback(struct mScriptContext* context, const char* callback, struct mScriptValue* fn) {
uint32_t mScriptContextAddCallback(struct mScriptContext* context, const char* callback, struct mScriptValue* fn) {
if (fn->type->base != mSCRIPT_TYPE_FUNCTION) {
return;
return 0;
}
struct mScriptValue* list = HashTableLookup(&context->callbacks, callback);
if (!list) {
list = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST);
HashTableInsert(&context->callbacks, callback, list);
}
struct mScriptCallbackInfo* info = malloc(sizeof(*info));
// Steal the string from the table key, since it's guaranteed to outlive this struct
struct TableIterator iter;
HashTableIteratorLookup(&context->callbacks, &iter, callback);
info->callback = HashTableIteratorGetKey(&context->callbacks, &iter);
info->id = mScriptListSize(list->value.list);
mScriptValueWrap(fn, mScriptListAppend(list->value.list));
while (true) {
uint32_t id = context->nextCallbackId;
++context->nextCallbackId;
if (TableLookup(&context->callbackId, id)) {
continue;
}
TableInsert(&context->callbackId, id, info);
return id;
}
}
void mScriptContextRemoveCallback(struct mScriptContext* context, uint32_t cbid) {
struct mScriptCallbackInfo* info = TableLookup(&context->callbackId, cbid);
if (!info) {
return;
}
struct mScriptValue* list = HashTableLookup(&context->callbacks, info->callback);
if (!list) {
return;
}
if (info->id >= mScriptListSize(list->value.list)) {
return;
}
struct mScriptValue* fn = mScriptValueUnwrap(mScriptListGetPointer(list->value.list, info->id));
mScriptValueDeref(fn);
mScriptListGetPointer(list->value.list, info->id)->type = NULL;
}
void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants) {

View File

@ -19,16 +19,22 @@ struct mScriptCallbackManager {
struct mScriptContext* context;
};
static void _mScriptCallbackAdd(struct mScriptCallbackManager* adapter, struct mScriptString* name, struct mScriptValue* fn) {
static uint32_t _mScriptCallbackAdd(struct mScriptCallbackManager* adapter, struct mScriptString* name, struct mScriptValue* fn) {
if (fn->type->base == mSCRIPT_TYPE_WRAPPER) {
fn = mScriptValueUnwrap(fn);
}
mScriptContextAddCallback(adapter->context, name->buffer, fn);
uint32_t id = mScriptContextAddCallback(adapter->context, name->buffer, fn);
mScriptValueDeref(fn);
return id;
}
static void _mScriptCallbackRemove(struct mScriptCallbackManager* adapter, uint32_t id) {
mScriptContextRemoveCallback(adapter->context, id);
}
mSCRIPT_DECLARE_STRUCT(mScriptCallbackManager);
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackManager, add, _mScriptCallbackAdd, 2, STR, callback, WRAPPER, function);
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCallbackManager, U32, add, _mScriptCallbackAdd, 2, STR, callback, WRAPPER, function);
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackManager, remove, _mScriptCallbackRemove, 1, U32, cbid);
static uint64_t mScriptMakeBitmask(struct mScriptList* list) {
size_t i;
@ -76,8 +82,10 @@ mSCRIPT_DEFINE_STRUCT(mScriptCallbackManager)
"- **start**: The emulation has started\n"
"- **stop**: The emulation has voluntarily shut down\n"
)
mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type")
mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type. The returned id can be used to remove it later")
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, add)
mSCRIPT_DEFINE_DOCSTRING("Remove a callback with the previously retuned id")
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, remove)
mSCRIPT_DEFINE_END;
void mScriptContextAttachStdlib(struct mScriptContext* context) {

View File

@ -7,6 +7,7 @@
#include <mgba/internal/script/lua.h>
#include <mgba/script/context.h>
#include <mgba/script/macros.h>
#include <mgba/script/types.h>
#define SETUP_LUA \
@ -85,7 +86,36 @@ M_TEST_DEFINE(bitUnmask) {
mScriptContextDeinit(&context);
}
M_TEST_DEFINE(callbacks) {
SETUP_LUA;
TEST_PROGRAM(
"val = 0\n"
"function cb()\n"
" val = val + 1\n"
"end\n"
"id = callbacks:add('test', cb)\n"
"assert(id)"
);
TEST_VALUE(S32, "val", 0);
mScriptContextTriggerCallback(&context, "test");
TEST_VALUE(S32, "val", 1);
mScriptContextTriggerCallback(&context, "test");
TEST_VALUE(S32, "val", 2);
TEST_PROGRAM("callbacks:remove(id)");
mScriptContextTriggerCallback(&context, "test");
TEST_VALUE(S32, "val", 2);
mScriptContextDeinit(&context);
}
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptStdlib,
cmocka_unit_test(bitMask),
cmocka_unit_test(bitUnmask),
cmocka_unit_test(callbacks),
)

View File

@ -249,6 +249,9 @@ void _allocList(struct mScriptValue* val) {
void _freeList(struct mScriptValue* val) {
size_t i;
for (i = 0; i < mScriptListSize(val->value.list); ++i) {
if (val->type) {
continue;
}
struct mScriptValue* unwrapped = mScriptValueUnwrap(mScriptListGetPointer(val->value.list, i));
if (unwrapped) {
mScriptValueDeref(unwrapped);