Scripting: Add type-overloadable setters

This commit is contained in:
Vicki Pfau 2023-02-03 23:08:07 -08:00
parent f74db92ccd
commit 004f68496f
4 changed files with 229 additions and 7 deletions

View File

@ -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)

View File

@ -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 {

View File

@ -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),
)

View File

@ -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) {