diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 16e8b3eb3..08925f514 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -208,7 +208,7 @@ CXX_GUARD_START .size = sizeof(struct STRUCT), \ .name = "struct::" #STRUCT, \ .alloc = NULL, \ - .free = NULL, \ + .free = mScriptObjectFree, \ .cast = mScriptObjectCast, \ .constType = &mSTStructConst_ ## STRUCT, \ }; \ @@ -355,7 +355,6 @@ CXX_GUARD_START return true; \ } \ - #define mSCRIPT_DECLARE_STRUCT_D_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \ mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__) @@ -368,8 +367,8 @@ CXX_GUARD_START #define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD(TYPE, NAME, NPARAMS, ...) \ mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__) -#define mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, EXPORTED_NAME, NAME) { \ - .type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \ +#define _mSCRIPT_DEFINE_STRUCT_BINDING(INIT_TYPE, TYPE, EXPORTED_NAME, NAME) { \ + .type = mSCRIPT_CLASS_INIT_ ## INIT_TYPE, \ .info = { \ .member = { \ .name = #EXPORTED_NAME, \ @@ -378,9 +377,15 @@ CXX_GUARD_START }, \ }, +#define mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, EXPORTED_NAME, NAME) \ + _mSCRIPT_DEFINE_STRUCT_BINDING(INSTANCE_MEMBER, TYPE, EXPORTED_NAME, NAME) + #define mSCRIPT_DEFINE_STRUCT_METHOD(TYPE, NAME) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, NAME, NAME) -#define mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TYPE) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, _get, _get) +#define mSCRIPT_DEFINE_STRUCT_INIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(INIT, TYPE, _init, _init) +#define mSCRIPT_DEFINE_STRUCT_DEINIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, _deinit) +#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_CAST_TO_MEMBER(TYPE, CAST_TYPE, MEMBER) { \ .type = mSCRIPT_CLASS_INIT_CAST_TO_MEMBER, \ @@ -495,6 +500,10 @@ enum mScriptClassInitType { mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, mSCRIPT_CLASS_INIT_INHERIT, mSCRIPT_CLASS_INIT_CAST_TO_MEMBER, + mSCRIPT_CLASS_INIT_INIT, + mSCRIPT_CLASS_INIT_DEINIT, + mSCRIPT_CLASS_INIT_GET, + mSCRIPT_CLASS_INIT_SET, }; enum { @@ -560,6 +569,10 @@ struct mScriptTypeClass { const struct mScriptType* parent; struct Table instanceMembers; struct Table castToMembers; + struct mScriptClassMember* alloc; // TODO + struct mScriptClassMember* free; + struct mScriptClassMember* get; + struct mScriptClassMember* set; // TODO }; struct mScriptValue; @@ -643,6 +656,7 @@ bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScri bool mScriptObjectGetConst(const struct mScriptValue* obj, const char* member, struct mScriptValue*); bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScriptValue*); bool mScriptObjectCast(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) ; +void mScriptObjectFree(struct mScriptValue* obj); bool mScriptPopS32(struct mScriptList* list, int32_t* out); bool mScriptPopU32(struct mScriptList* list, uint32_t* out); diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 475514599..f019b9f91 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -38,6 +38,10 @@ struct TestD { struct TestE { }; +struct TestF { + int* ref; +}; + static int32_t testAi0(struct TestA* a) { return a->i; } @@ -67,6 +71,10 @@ static int32_t testGet(struct TestE* e, const char* name) { return name[0]; } +static void testDeinit(struct TestF* f) { + ++*f->ref; +} + #define MEMBER_A_DOCSTRING "Member a" mSCRIPT_DECLARE_STRUCT(TestA); @@ -124,6 +132,13 @@ mSCRIPT_DEFINE_STRUCT(TestE) mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TestE) mSCRIPT_DEFINE_END; +mSCRIPT_DECLARE_STRUCT(TestF); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestF, _deinit, testDeinit, 0); + +mSCRIPT_DEFINE_STRUCT(TestF) + mSCRIPT_DEFINE_STRUCT_DEINIT(TestF) +mSCRIPT_DEFINE_END; + M_TEST_DEFINE(testALayout) { struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; assert_false(cls->init); @@ -910,6 +925,23 @@ M_TEST_DEFINE(testEGet) { assert_false(cls->init); } +M_TEST_DEFINE(testFDeinit) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestF)->details.cls; + + int ref = 0; + struct TestF* s = calloc(1, sizeof(struct TestF)); + s->ref = &ref; + struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(TestF)); + val->value.opaque = s; + val->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; + mScriptValueDeref(val); + assert_int_equal(ref, 1); + + assert_true(cls->init); + mScriptClassDeinit(cls); + assert_false(cls->init); +} + M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testALayout), cmocka_unit_test(testASignatures), @@ -924,4 +956,5 @@ M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testDGet), cmocka_unit_test(testDSet), cmocka_unit_test(testEGet), + cmocka_unit_test(testFDeinit), ) diff --git a/src/script/types.c b/src/script/types.c index cea72ad71..f2c5efc60 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -806,6 +806,38 @@ static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScript case mSCRIPT_CLASS_INIT_CAST_TO_MEMBER: HashTableInsert(&cls->castToMembers, detail->info.castMember.type->name, (char*) detail->info.castMember.member); break; + case mSCRIPT_CLASS_INIT_INIT: + cls->alloc = calloc(1, sizeof(*member)); + memcpy(cls->alloc, &detail->info.member, sizeof(*member)); + if (docstring) { + cls->alloc->docstring = docstring; + docstring = NULL; + } + break; + case mSCRIPT_CLASS_INIT_DEINIT: + cls->free = calloc(1, sizeof(*member)); + memcpy(cls->free, &detail->info.member, sizeof(*member)); + if (docstring) { + cls->free->docstring = docstring; + docstring = NULL; + } + break; + case mSCRIPT_CLASS_INIT_GET: + cls->get = calloc(1, sizeof(*member)); + memcpy(cls->get, &detail->info.member, sizeof(*member)); + if (docstring) { + cls->get->docstring = docstring; + docstring = NULL; + } + break; + case mSCRIPT_CLASS_INIT_SET: + cls->set = calloc(1, sizeof(*member)); + memcpy(cls->set, &detail->info.member, sizeof(*member)); + if (docstring) { + cls->set->docstring = docstring; + docstring = NULL; + } + break; } } } @@ -817,6 +849,10 @@ void mScriptClassInit(struct mScriptTypeClass* cls) { HashTableInit(&cls->instanceMembers, 0, free); HashTableInit(&cls->castToMembers, 0, NULL); + cls->alloc = NULL; + cls->free = NULL; + cls->get = NULL; + cls->set = NULL; _mScriptClassInit(cls, cls->details, false); cls->init = true; @@ -934,7 +970,7 @@ bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScri struct mScriptClassMember* m = HashTableLookup(&cls->instanceMembers, member); if (!m) { struct mScriptValue getMember; - m = HashTableLookup(&cls->instanceMembers, "_get"); + m = cls->get; if (!m || !_accessRawMember(m, obj->value.opaque, obj->type->isConst, &getMember)) { return false; } @@ -1084,6 +1120,30 @@ bool mScriptObjectCast(const struct mScriptValue* input, const struct mScriptTyp return false; } +void mScriptObjectFree(struct mScriptValue* value) { + if (value->type->base != mSCRIPT_TYPE_OBJECT) { + return; + } + if (value->flags & mSCRIPT_VALUE_FLAG_FREE_BUFFER) { + mScriptClassInit(value->type->details.cls); + if (value->type->details.cls->free) { + struct mScriptValue deinitMember; + if (_accessRawMember(value->type->details.cls->free, value->value.opaque, value->type->isConst, &deinitMember)) { + struct mScriptFrame frame; + mScriptFrameInit(&frame); + struct mScriptValue* this = mScriptListAppend(&frame.arguments); + this->type = mSCRIPT_TYPE_MS_WRAPPER; + this->refs = mSCRIPT_VALUE_UNREF; + this->flags = 0; + this->value.opaque = value; + mScriptInvoke(&deinitMember, &frame); + mScriptFrameDeinit(&frame); + } + } + free(value->value.opaque); + } +} + bool mScriptPopS32(struct mScriptList* list, int32_t* out) { mSCRIPT_POP(list, S32, val); *out = val;