mirror of https://github.com/mgba-emu/mgba.git
Scripting: Add type-overloadable setters
This commit is contained in:
parent
f74db92ccd
commit
004f68496f
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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),
|
||||
)
|
||||
|
|
|
@ -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,8 +1456,37 @@ bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScri
|
|||
|
||||
struct mScriptClassMember* m = HashTableLookup(&cls->instanceMembers, member);
|
||||
if (!m) {
|
||||
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);
|
||||
if (m->type != val->type) {
|
||||
|
|
Loading…
Reference in New Issue