mirror of https://github.com/mgba-emu/mgba.git
Scripting: Default "get" handling a la Python and casting a struct into one of its members
This commit is contained in:
parent
aa91ffabfd
commit
deff3585bd
|
@ -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, \
|
.type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \
|
||||||
.info = { \
|
.info = { \
|
||||||
.member = { \
|
.member = { \
|
||||||
.name = #NAME, \
|
.name = #EXPORTED_NAME, \
|
||||||
.type = mSCRIPT_TYPE_MS_ ## TYPE, \
|
.type = mSCRIPT_TYPE_MS_ ## TYPE, \
|
||||||
.offset = offsetof(struct STRUCT, NAME) \
|
.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) { \
|
#define mSCRIPT_DEFINE_INHERIT(PARENT) { \
|
||||||
.type = mSCRIPT_CLASS_INIT_INHERIT, \
|
.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_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_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } }
|
||||||
|
|
||||||
#define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \
|
#define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \
|
||||||
|
@ -480,6 +494,7 @@ enum mScriptClassInitType {
|
||||||
mSCRIPT_CLASS_INIT_DOCSTRING,
|
mSCRIPT_CLASS_INIT_DOCSTRING,
|
||||||
mSCRIPT_CLASS_INIT_INSTANCE_MEMBER,
|
mSCRIPT_CLASS_INIT_INSTANCE_MEMBER,
|
||||||
mSCRIPT_CLASS_INIT_INHERIT,
|
mSCRIPT_CLASS_INIT_INHERIT,
|
||||||
|
mSCRIPT_CLASS_INIT_CAST_TO_MEMBER,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -524,12 +539,18 @@ struct mScriptClassMember {
|
||||||
size_t offset;
|
size_t offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct mScriptClassCastMember {
|
||||||
|
const struct mScriptType* type;
|
||||||
|
const char* member;
|
||||||
|
};
|
||||||
|
|
||||||
struct mScriptClassInitDetails {
|
struct mScriptClassInitDetails {
|
||||||
enum mScriptClassInitType type;
|
enum mScriptClassInitType type;
|
||||||
union {
|
union {
|
||||||
const char* comment;
|
const char* comment;
|
||||||
const struct mScriptType* parent;
|
const struct mScriptType* parent;
|
||||||
struct mScriptClassMember member;
|
struct mScriptClassMember member;
|
||||||
|
struct mScriptClassCastMember castMember;
|
||||||
} info;
|
} info;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -538,6 +559,7 @@ struct mScriptTypeClass {
|
||||||
const struct mScriptClassInitDetails* details;
|
const struct mScriptClassInitDetails* details;
|
||||||
const struct mScriptType* parent;
|
const struct mScriptType* parent;
|
||||||
struct Table instanceMembers;
|
struct Table instanceMembers;
|
||||||
|
struct Table castToMembers;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mScriptValue;
|
struct mScriptValue;
|
||||||
|
|
|
@ -35,6 +35,9 @@ struct TestD {
|
||||||
struct TestC b;
|
struct TestC b;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TestE {
|
||||||
|
};
|
||||||
|
|
||||||
static int32_t testAi0(struct TestA* a) {
|
static int32_t testAi0(struct TestA* a) {
|
||||||
return a->i;
|
return a->i;
|
||||||
}
|
}
|
||||||
|
@ -59,6 +62,11 @@ static void testAv1(struct TestA* a, int b) {
|
||||||
a->i += 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"
|
#define MEMBER_A_DOCSTRING "Member a"
|
||||||
|
|
||||||
mSCRIPT_DECLARE_STRUCT(TestA);
|
mSCRIPT_DECLARE_STRUCT(TestA);
|
||||||
|
@ -109,6 +117,13 @@ mSCRIPT_DEFINE_STRUCT(TestD)
|
||||||
mSCRIPT_DEFINE_STRUCT_MEMBER(TestD, S(TestC), b)
|
mSCRIPT_DEFINE_STRUCT_MEMBER(TestD, S(TestC), b)
|
||||||
mSCRIPT_DEFINE_END;
|
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) {
|
M_TEST_DEFINE(testALayout) {
|
||||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls;
|
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls;
|
||||||
assert_false(cls->init);
|
assert_false(cls->init);
|
||||||
|
@ -258,6 +273,8 @@ M_TEST_DEFINE(testAGet) {
|
||||||
assert_true(mScriptObjectGet(&sval, "hUnaligned", &val));
|
assert_true(mScriptObjectGet(&sval, "hUnaligned", &val));
|
||||||
assert_true(compare.type->equal(&compare, &val));
|
assert_true(compare.type->equal(&compare, &val));
|
||||||
|
|
||||||
|
assert_false(mScriptObjectGet(&sval, "unknown", &val));
|
||||||
|
|
||||||
assert_true(cls->init);
|
assert_true(cls->init);
|
||||||
mScriptClassDeinit(cls);
|
mScriptClassDeinit(cls);
|
||||||
assert_false(cls->init);
|
assert_false(cls->init);
|
||||||
|
@ -310,6 +327,8 @@ M_TEST_DEFINE(testASet) {
|
||||||
assert_false(mScriptObjectSet(&sval, "hUnaligned", &val));
|
assert_false(mScriptObjectSet(&sval, "hUnaligned", &val));
|
||||||
assert_int_equal(s.hUnaligned, 5);
|
assert_int_equal(s.hUnaligned, 5);
|
||||||
|
|
||||||
|
assert_false(mScriptObjectSet(&sval, "unknown", &val));
|
||||||
|
|
||||||
assert_true(cls->init);
|
assert_true(cls->init);
|
||||||
mScriptClassDeinit(cls);
|
mScriptClassDeinit(cls);
|
||||||
assert_false(cls->init);
|
assert_false(cls->init);
|
||||||
|
@ -868,6 +887,29 @@ M_TEST_DEFINE(testDSet) {
|
||||||
assert_false(cls->init);
|
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,
|
M_TEST_SUITE_DEFINE(mScriptClasses,
|
||||||
cmocka_unit_test(testALayout),
|
cmocka_unit_test(testALayout),
|
||||||
cmocka_unit_test(testASignatures),
|
cmocka_unit_test(testASignatures),
|
||||||
|
@ -880,4 +922,6 @@ M_TEST_SUITE_DEFINE(mScriptClasses,
|
||||||
cmocka_unit_test(testBSet),
|
cmocka_unit_test(testBSet),
|
||||||
cmocka_unit_test(testDLayout),
|
cmocka_unit_test(testDLayout),
|
||||||
cmocka_unit_test(testDGet),
|
cmocka_unit_test(testDGet),
|
||||||
cmocka_unit_test(testDSet))
|
cmocka_unit_test(testDSet),
|
||||||
|
cmocka_unit_test(testEGet),
|
||||||
|
)
|
||||||
|
|
|
@ -280,6 +280,9 @@ M_TEST_DEFINE(wrongConst) {
|
||||||
.variable = false
|
.variable = false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mScriptClassInit(mSCRIPT_TYPE_MS_S(Test)->details.cls);
|
||||||
|
mScriptClassInit(mSCRIPT_TYPE_MS_CS(Test)->details.cls);
|
||||||
|
|
||||||
mScriptFrameInit(&frame);
|
mScriptFrameInit(&frame);
|
||||||
mSCRIPT_PUSH(&frame.arguments, S(Test), &a);
|
mSCRIPT_PUSH(&frame.arguments, S(Test), &a);
|
||||||
signature.entries[0] = mSCRIPT_TYPE_MS_S(Test);
|
signature.entries[0] = mSCRIPT_TYPE_MS_S(Test);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include <mgba/script/types.h>
|
#include <mgba/script/types.h>
|
||||||
|
|
||||||
|
#include <mgba/script/context.h>
|
||||||
#include <mgba-util/hash.h>
|
#include <mgba-util/hash.h>
|
||||||
#include <mgba-util/table.h>
|
#include <mgba-util/table.h>
|
||||||
|
|
||||||
|
@ -793,6 +794,9 @@ static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScript
|
||||||
}
|
}
|
||||||
HashTableInsert(&cls->instanceMembers, member->name, member);
|
HashTableInsert(&cls->instanceMembers, member->name, member);
|
||||||
break;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
HashTableInit(&cls->instanceMembers, 0, free);
|
HashTableInit(&cls->instanceMembers, 0, free);
|
||||||
|
HashTableInit(&cls->castToMembers, 0, NULL);
|
||||||
|
|
||||||
_mScriptClassInit(cls, cls->details, false);
|
_mScriptClassInit(cls, cls->details, false);
|
||||||
|
|
||||||
|
@ -813,6 +818,7 @@ void mScriptClassDeinit(struct mScriptTypeClass* cls) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
HashTableDeinit(&cls->instanceMembers);
|
HashTableDeinit(&cls->instanceMembers);
|
||||||
|
HashTableDeinit(&cls->castToMembers);
|
||||||
cls->init = false;
|
cls->init = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -913,7 +919,26 @@ bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScri
|
||||||
|
|
||||||
struct mScriptClassMember* m = HashTableLookup(&cls->instanceMembers, member);
|
struct mScriptClassMember* m = HashTableLookup(&cls->instanceMembers, member);
|
||||||
if (!m) {
|
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);
|
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;
|
output->flags = 0;
|
||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue