From 004f68496f5071d68517042019234cf21403cc74 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 3 Feb 2023 23:08:07 -0800 Subject: [PATCH] Scripting: Add type-overloadable setters --- include/mgba/script/macros.h | 2 +- include/mgba/script/types.h | 2 +- src/script/test/classes.c | 103 ++++++++++++++++++++++++++++ src/script/types.c | 129 +++++++++++++++++++++++++++++++++-- 4 files changed, 229 insertions(+), 7 deletions(-) diff --git a/include/mgba/script/macros.h b/include/mgba/script/macros.h index 42710f9e8..bbde17b07 100644 --- a/include/mgba/script/macros.h +++ b/include/mgba/script/macros.h @@ -423,7 +423,7 @@ CXX_GUARD_START #define mSCRIPT_DEFINE_STRUCT_DEINIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, _deinit) #define mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(TYPE, NAME) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, NAME) #define mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(GET, TYPE, _get, _get) -#define mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(SET, TYPE, _set, _set) +#define mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TYPE, SETTER) _mSCRIPT_DEFINE_STRUCT_BINDING(SET, TYPE, SETTER, SETTER) #define mSCRIPT_DEFINE_DOC_STRUCT_METHOD(SCOPE, TYPE, NAME) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(doc_ ## TYPE, NAME, NAME) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 17b3da70e..76d5c1579 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -252,10 +252,10 @@ struct mScriptTypeClass { bool internal; struct Table instanceMembers; struct Table castToMembers; + struct Table setters; struct mScriptClassMember* alloc; // TODO struct mScriptClassMember* free; struct mScriptClassMember* get; - struct mScriptClassMember* set; // TODO }; struct mScriptType { diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 85afb627c..f4dec7722 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -46,6 +46,14 @@ struct TestF { int* ref; }; +struct TestG { + const char* name; + int64_t s; + uint64_t u; + double f; + const char* c; +}; + static int32_t testAi0(struct TestA* a) { return a->i; } @@ -83,6 +91,26 @@ static void testDeinit(struct TestF* f) { ++*f->ref; } +static void testSetS(struct TestG* g, const char* name, int64_t val) { + g->name = name; + g->s = val; +} + +static void testSetU(struct TestG* g, const char* name, uint64_t val) { + g->name = name; + g->u = val; +} + +static void testSetF(struct TestG* g, const char* name, double val) { + g->name = name; + g->f = val; +} + +static void testSetC(struct TestG* g, const char* name, const char* val) { + g->name = name; + g->c = val; +} + #define MEMBER_A_DOCSTRING "Member a" mSCRIPT_DECLARE_STRUCT(TestA); @@ -157,6 +185,19 @@ mSCRIPT_DEFINE_STRUCT(TestF) mSCRIPT_DEFINE_STRUCT_DEINIT(TestF) mSCRIPT_DEFINE_END; +mSCRIPT_DECLARE_STRUCT(TestG); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestG, setS, testSetS, 2, CHARP, name, S64, value); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestG, setU, testSetU, 2, CHARP, name, U64, value); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestG, setF, testSetF, 2, CHARP, name, F64, value); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestG, setC, testSetC, 2, CHARP, name, CHARP, value); + +mSCRIPT_DEFINE_STRUCT(TestG) + mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TestG, setS) + mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TestG, setU) + mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TestG, setF) + mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TestG, setC) +mSCRIPT_DEFINE_END; + M_TEST_DEFINE(testALayout) { struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; assert_false(cls->init); @@ -1032,6 +1073,67 @@ M_TEST_DEFINE(testFDeinit) { assert_false(cls->init); } +M_TEST_DEFINE(testGSet) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestG)->details.cls; + + struct TestG s = { + }; + + assert_int_equal(s.s, 0); + assert_int_equal(s.u, 0); + assert_float_equal(s.f, 0, 0); + assert_null(s.c); + + struct mScriptValue sval = mSCRIPT_MAKE_S(TestG, &s); + struct mScriptValue val; + struct mScriptValue* pval; + + val = mSCRIPT_MAKE_S64(1); + assert_true(mScriptObjectSet(&sval, "a", &val)); + assert_int_equal(s.s, 1); + assert_string_equal(s.name, "a"); + + val = mSCRIPT_MAKE_U64(2); + assert_true(mScriptObjectSet(&sval, "b", &val)); + assert_int_equal(s.u, 2); + assert_string_equal(s.name, "b"); + + val = mSCRIPT_MAKE_F64(1.5); + assert_true(mScriptObjectSet(&sval, "c", &val)); + assert_float_equal(s.f, 1.5, 0); + assert_string_equal(s.name, "c"); + + val = mSCRIPT_MAKE_CHARP("hello"); + assert_true(mScriptObjectSet(&sval, "d", &val)); + assert_string_equal(s.c, "hello"); + assert_string_equal(s.name, "d"); + + val = mSCRIPT_MAKE_S32(3); + assert_true(mScriptObjectSet(&sval, "a", &val)); + assert_int_equal(s.s, 3); + + val = mSCRIPT_MAKE_S16(4); + assert_true(mScriptObjectSet(&sval, "a", &val)); + assert_int_equal(s.s, 4); + + val = mSCRIPT_MAKE_S8(5); + assert_true(mScriptObjectSet(&sval, "a", &val)); + assert_int_equal(s.s, 5); + + val = mSCRIPT_MAKE_BOOL(false); + assert_true(mScriptObjectSet(&sval, "a", &val)); + assert_int_equal(s.u, 0); + + pval = mScriptStringCreateFromASCII("goodbye"); + assert_true(mScriptObjectSet(&sval, "a", pval)); + assert_string_equal(s.c, "goodbye"); + mScriptValueDeref(pval); + + assert_true(cls->init); + mScriptClassDeinit(cls); + assert_false(cls->init); +} + M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testALayout), cmocka_unit_test(testASignatures), @@ -1047,4 +1149,5 @@ M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testDSet), cmocka_unit_test(testEGet), cmocka_unit_test(testFDeinit), + cmocka_unit_test(testGSet), ) diff --git a/src/script/types.c b/src/script/types.c index 4ee851812..a05f6d252 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -1149,12 +1149,16 @@ static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScript } break; case mSCRIPT_CLASS_INIT_SET: - cls->set = calloc(1, sizeof(*member)); - memcpy(cls->set, &detail->info.member, sizeof(*member)); + member = calloc(1, sizeof(*member)); + memcpy(member, &detail->info.member, sizeof(*member)); if (docstring) { - cls->set->docstring = docstring; + member->docstring = docstring; docstring = NULL; } + if (detail->info.member.type->details.function.parameters.count != 3) { + abort(); + } + HashTableInsert(&cls->setters, detail->info.member.type->details.function.parameters.entries[2]->name, member); break; case mSCRIPT_CLASS_INIT_INTERNAL: cls->internal = true; @@ -1169,11 +1173,11 @@ void mScriptClassInit(struct mScriptTypeClass* cls) { } HashTableInit(&cls->instanceMembers, 0, free); HashTableInit(&cls->castToMembers, 0, NULL); + HashTableInit(&cls->setters, 0, free); cls->alloc = NULL; cls->free = NULL; cls->get = NULL; - cls->set = NULL; _mScriptClassInit(cls, cls->details, false); cls->init = true; @@ -1185,6 +1189,7 @@ void mScriptClassDeinit(struct mScriptTypeClass* cls) { } HashTableDeinit(&cls->instanceMembers); HashTableDeinit(&cls->castToMembers); + HashTableDeinit(&cls->setters); cls->init = false; } @@ -1352,6 +1357,91 @@ bool mScriptObjectGetConst(const struct mScriptValue* obj, const char* member, s return _accessRawMember(m, obj->value.opaque, true, val); } +static struct mScriptClassMember* _findSetter(const struct mScriptTypeClass* cls, const struct mScriptType* type) { + struct mScriptClassMember* m = HashTableLookup(&cls->setters, type->name); + if (m) { + return m; + } + + switch (type->base) { + case mSCRIPT_TYPE_SINT: + if (type->size < 2) { + m = HashTableLookup(&cls->setters, mSCRIPT_TYPE_MS_S16->name); + if (m) { + return m; + } + } + if (type->size < 4) { + m = HashTableLookup(&cls->setters, mSCRIPT_TYPE_MS_S32->name); + if (m) { + return m; + } + } + if (type->size < 8) { + m = HashTableLookup(&cls->setters, mSCRIPT_TYPE_MS_S64->name); + if (m) { + return m; + } + } + break; + case mSCRIPT_TYPE_UINT: + if (type->size < 2) { + m = HashTableLookup(&cls->setters, mSCRIPT_TYPE_MS_U16->name); + if (m) { + return m; + } + } + if (type->size < 4) { + m = HashTableLookup(&cls->setters, mSCRIPT_TYPE_MS_U32->name); + if (m) { + return m; + } + } + if (type->size < 8) { + m = HashTableLookup(&cls->setters, mSCRIPT_TYPE_MS_U64->name); + if (m) { + return m; + } + } + break; + case mSCRIPT_TYPE_FLOAT: + if (type->size < 8) { + m = HashTableLookup(&cls->setters, mSCRIPT_TYPE_MS_F64->name); + if (m) { + return m; + } + } + break; + case mSCRIPT_TYPE_STRING: + if (type == mSCRIPT_TYPE_MS_STR) { + m = HashTableLookup(&cls->setters, mSCRIPT_TYPE_MS_CHARP->name); + if (m) { + return m; + } + m = HashTableLookup(&cls->setters, mSCRIPT_TYPE_MS_WSTR->name); + if (m) { + return m; + } + } + break; + case mSCRIPT_TYPE_LIST: + m = HashTableLookup(&cls->setters, mSCRIPT_TYPE_MS_WLIST->name); + if (m) { + return m; + } + break; + case mSCRIPT_TYPE_TABLE: + m = HashTableLookup(&cls->setters, mSCRIPT_TYPE_MS_WTABLE->name); + if (m) { + return m; + } + break; + default: + break; + } + return NULL; +} + bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScriptValue* val) { if (obj->type->base != mSCRIPT_TYPE_OBJECT || obj->type->isConst) { return false; @@ -1366,7 +1456,36 @@ bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScri struct mScriptClassMember* m = HashTableLookup(&cls->instanceMembers, member); if (!m) { - return false; + if (val->type->base == mSCRIPT_TYPE_WRAPPER) { + val = mScriptValueUnwrap(val); + } + struct mScriptValue setMember; + m = _findSetter(cls, val->type); + if (!m || !_accessRawMember(m, obj->value.opaque, obj->type->isConst, &setMember)) { + return false; + } + struct mScriptFrame frame; + mScriptFrameInit(&frame); + struct mScriptValue* this = mScriptListAppend(&frame.arguments); + this->type = obj->type; + this->refs = mSCRIPT_VALUE_UNREF; + this->flags = 0; + this->value.opaque = obj->value.opaque; + mSCRIPT_PUSH(&frame.arguments, CHARP, member); + mScriptValueWrap(val, mScriptListAppend(&frame.arguments)); + bool needsDeref = mScriptListGetPointer(&frame.arguments, 2)->type->base == mSCRIPT_TYPE_WRAPPER; + if (!mScriptInvoke(&setMember, &frame) || mScriptListSize(&frame.returnValues) != 0) { + mScriptFrameDeinit(&frame); + if (needsDeref) { + mScriptValueDeref(val); + } + return false; + } + mScriptFrameDeinit(&frame); + if (needsDeref) { + mScriptValueDeref(val); + } + return true; } void* rawMember = (void *)((uintptr_t) obj->value.opaque + m->offset);