diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 421a7bfb4..c6b859acb 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -253,17 +253,19 @@ CXX_GUARD_START } \ }, -#define mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, NAME) { \ +#define mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, EXPORTED_NAME, NAME) { \ .type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \ .info = { \ .member = { \ - .name = #NAME, \ + .name = #EXPORTED_NAME, \ .type = mSCRIPT_TYPE_MS_ ## TYPE, \ .offset = offsetof(struct STRUCT, NAME) \ } \ } \ }, +#define mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, NAME) \ + mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, NAME, NAME) #define mSCRIPT_DEFINE_INHERIT(PARENT) { \ .type = mSCRIPT_CLASS_INIT_INHERIT, \ @@ -378,6 +380,18 @@ CXX_GUARD_START #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_CAST_TO_MEMBER(TYPE, CAST_TYPE, MEMBER) { \ + .type = mSCRIPT_CLASS_INIT_CAST_TO_MEMBER, \ + .info = { \ + .castMember = { \ + .type = mSCRIPT_TYPE_MS_ ## CAST_TYPE, \ + .member = #MEMBER \ + } \ + }, \ +}, + #define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } } #define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \ @@ -480,6 +494,7 @@ enum mScriptClassInitType { mSCRIPT_CLASS_INIT_DOCSTRING, mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, mSCRIPT_CLASS_INIT_INHERIT, + mSCRIPT_CLASS_INIT_CAST_TO_MEMBER, }; enum { @@ -524,12 +539,18 @@ struct mScriptClassMember { size_t offset; }; +struct mScriptClassCastMember { + const struct mScriptType* type; + const char* member; +}; + struct mScriptClassInitDetails { enum mScriptClassInitType type; union { const char* comment; const struct mScriptType* parent; struct mScriptClassMember member; + struct mScriptClassCastMember castMember; } info; }; @@ -538,6 +559,7 @@ struct mScriptTypeClass { const struct mScriptClassInitDetails* details; const struct mScriptType* parent; struct Table instanceMembers; + struct Table castToMembers; }; struct mScriptValue; diff --git a/src/script/test/classes.c b/src/script/test/classes.c index aa1060e93..475514599 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -35,6 +35,9 @@ struct TestD { struct TestC b; }; +struct TestE { +}; + static int32_t testAi0(struct TestA* a) { return a->i; } @@ -59,6 +62,11 @@ static void testAv1(struct TestA* a, int b) { a->i += b; } +static int32_t testGet(struct TestE* e, const char* name) { + UNUSED(e); + return name[0]; +} + #define MEMBER_A_DOCSTRING "Member a" mSCRIPT_DECLARE_STRUCT(TestA); @@ -109,6 +117,13 @@ mSCRIPT_DEFINE_STRUCT(TestD) mSCRIPT_DEFINE_STRUCT_MEMBER(TestD, S(TestC), b) mSCRIPT_DEFINE_END; +mSCRIPT_DECLARE_STRUCT(TestE); +mSCRIPT_DECLARE_STRUCT_METHOD(TestE, S32, _get, testGet, 1, CHARP, name); + +mSCRIPT_DEFINE_STRUCT(TestE) + mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TestE) +mSCRIPT_DEFINE_END; + M_TEST_DEFINE(testALayout) { struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls; assert_false(cls->init); @@ -258,6 +273,8 @@ M_TEST_DEFINE(testAGet) { assert_true(mScriptObjectGet(&sval, "hUnaligned", &val)); assert_true(compare.type->equal(&compare, &val)); + assert_false(mScriptObjectGet(&sval, "unknown", &val)); + assert_true(cls->init); mScriptClassDeinit(cls); assert_false(cls->init); @@ -310,6 +327,8 @@ M_TEST_DEFINE(testASet) { assert_false(mScriptObjectSet(&sval, "hUnaligned", &val)); assert_int_equal(s.hUnaligned, 5); + assert_false(mScriptObjectSet(&sval, "unknown", &val)); + assert_true(cls->init); mScriptClassDeinit(cls); assert_false(cls->init); @@ -868,6 +887,29 @@ M_TEST_DEFINE(testDSet) { assert_false(cls->init); } +M_TEST_DEFINE(testEGet) { + struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestE)->details.cls; + + struct TestE s = { + }; + + struct mScriptValue sval = mSCRIPT_MAKE_S(TestE, &s); + struct mScriptValue val; + struct mScriptValue compare; + + compare = mSCRIPT_MAKE_S32('a'); + assert_true(mScriptObjectGet(&sval, "a", &val)); + assert_true(compare.type->equal(&compare, &val)); + + compare = mSCRIPT_MAKE_S32('b'); + assert_true(mScriptObjectGet(&sval, "b", &val)); + assert_true(compare.type->equal(&compare, &val)); + + assert_true(cls->init); + mScriptClassDeinit(cls); + assert_false(cls->init); +} + M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testALayout), cmocka_unit_test(testASignatures), @@ -880,4 +922,6 @@ M_TEST_SUITE_DEFINE(mScriptClasses, cmocka_unit_test(testBSet), cmocka_unit_test(testDLayout), cmocka_unit_test(testDGet), - cmocka_unit_test(testDSet)) + cmocka_unit_test(testDSet), + cmocka_unit_test(testEGet), +) diff --git a/src/script/test/types.c b/src/script/test/types.c index ed1f26a79..dd0e3cdff 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -280,6 +280,9 @@ M_TEST_DEFINE(wrongConst) { .variable = false }; + mScriptClassInit(mSCRIPT_TYPE_MS_S(Test)->details.cls); + mScriptClassInit(mSCRIPT_TYPE_MS_CS(Test)->details.cls); + mScriptFrameInit(&frame); mSCRIPT_PUSH(&frame.arguments, S(Test), &a); signature.entries[0] = mSCRIPT_TYPE_MS_S(Test); diff --git a/src/script/types.c b/src/script/types.c index 4ff7038e8..33ea90f25 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include #include @@ -793,6 +794,9 @@ static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScript } HashTableInsert(&cls->instanceMembers, member->name, member); break; + case mSCRIPT_CLASS_INIT_CAST_TO_MEMBER: + HashTableInsert(&cls->castToMembers, detail->info.castMember.type->name, (char*) detail->info.castMember.member); + break; } } } @@ -802,6 +806,7 @@ void mScriptClassInit(struct mScriptTypeClass* cls) { return; } HashTableInit(&cls->instanceMembers, 0, free); + HashTableInit(&cls->castToMembers, 0, NULL); _mScriptClassInit(cls, cls->details, false); @@ -813,6 +818,7 @@ void mScriptClassDeinit(struct mScriptTypeClass* cls) { return; } HashTableDeinit(&cls->instanceMembers); + HashTableDeinit(&cls->castToMembers); cls->init = false; } @@ -913,7 +919,26 @@ bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScri struct mScriptClassMember* m = HashTableLookup(&cls->instanceMembers, member); if (!m) { - return false; + struct mScriptValue getMember; + m = HashTableLookup(&cls->instanceMembers, "_get"); + if (!m || !_accessRawMember(m, obj->value.opaque, obj->type->isConst, &getMember)) { + 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; + mSCRIPT_PUSH(&frame.arguments, CHARP, member); + if (!mScriptInvoke(&getMember, &frame) || mScriptListSize(&frame.returnValues) != 1) { + mScriptFrameDeinit(&frame); + return false; + } + memcpy(val, mScriptListGetPointer(&frame.returnValues, 0), sizeof(*val)); + mScriptFrameDeinit(&frame); + return true; } return _accessRawMember(m, obj->value.opaque, obj->type->isConst, val); @@ -1027,6 +1052,21 @@ bool mScriptObjectCast(const struct mScriptValue* input, const struct mScriptTyp output->flags = 0; return true; } + if (input->type->base != mSCRIPT_TYPE_OBJECT) { + return false; + } + const char* member = HashTableLookup(&input->type->details.cls->castToMembers, type->name); + if (member) { + struct mScriptValue cast; + if (!mScriptObjectGetConst(input, member, &cast)) { + return false; + } + if (cast.type == type) { + memcpy(output, &cast, sizeof(*output)); + return true; + } + return mScriptCast(type, &cast, output); + } return false; }