Scripting: Add basic inheritance + struct struct member access + const casting

This commit is contained in:
Vicki Pfau 2022-04-29 16:01:45 -07:00
parent 9ddada00f2
commit 7c9ea1ec9b
3 changed files with 418 additions and 32 deletions

View File

@ -202,6 +202,7 @@ CXX_GUARD_START
.alloc = NULL, \
.free = NULL, \
.cast = _mSTStructCast_ ## STRUCT, \
.constType = &mSTStructConst_ ## STRUCT, \
}; \
const struct mScriptType mSTStructConst_ ## STRUCT = { \
.base = mSCRIPT_TYPE_OBJECT, \
@ -255,6 +256,13 @@ CXX_GUARD_START
}, \
},
#define mSCRIPT_DEFINE_INHERIT(PARENT) { \
.type = mSCRIPT_CLASS_INIT_INHERIT, \
.info = { \
.parent = mSCRIPT_TYPE_MS_S(PARENT) \
} \
},
#define _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, ...) \
_mDEFER(_mDEFER(_mCAT(mSCRIPT_POP_, _mSUCC ## NPARAMS)) (&frame->arguments, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__))); \
if (mScriptListSize(&frame->arguments)) { \
@ -493,7 +501,7 @@ struct mScriptClassInitDetails {
enum mScriptClassInitType type;
union {
const char* comment;
const struct mScriptTypeClass* parent;
const struct mScriptType* parent;
struct mScriptClassMember member;
} info;
};
@ -501,6 +509,7 @@ struct mScriptClassInitDetails {
struct mScriptTypeClass {
bool init;
const struct mScriptClassInitDetails* details;
const struct mScriptType* parent;
struct Table staticMembers;
struct Table instanceMembers;
};
@ -518,6 +527,7 @@ struct mScriptType {
struct mScriptTypeClass* cls;
void* opaque;
} details;
const struct mScriptType* constType;
void (*alloc)(struct mScriptValue*);
void (*free)(struct mScriptValue*);
uint32_t (*hash)(const struct mScriptValue*);

View File

@ -21,6 +21,20 @@ struct TestA {
int32_t (*icfn1)(const struct TestA*, int);
};
struct TestB {
struct TestA d;
int32_t i3;
};
struct TestC {
int32_t i;
};
struct TestD {
struct TestC a;
struct TestC b;
};
static int32_t testAi0(struct TestA* a) {
return a->i;
}
@ -87,7 +101,24 @@ mSCRIPT_DEFINE_STRUCT(TestA)
mSCRIPT_DEFINE_STATIC_MEMBER(S16, s_hUnaligned)
mSCRIPT_DEFINE_END;
mSCRIPT_DEFINE_STRUCT(TestB)
mSCRIPT_DEFINE_INHERIT(TestA)
mSCRIPT_DEFINE_STRUCT_MEMBER(TestB, S32, i3)
mSCRIPT_DEFINE_END;
mSCRIPT_DEFINE_STRUCT(TestC)
mSCRIPT_DEFINE_STRUCT_MEMBER(TestC, S32, i)
mSCRIPT_DEFINE_END;
mSCRIPT_DEFINE_STRUCT(TestD)
mSCRIPT_DEFINE_STRUCT_MEMBER(TestD, S(TestC), a)
mSCRIPT_DEFINE_STRUCT_MEMBER(TestD, S(TestC), b)
mSCRIPT_DEFINE_END;
mSCRIPT_EXPORT_STRUCT(TestA);
mSCRIPT_EXPORT_STRUCT(TestB);
mSCRIPT_EXPORT_STRUCT(TestC);
mSCRIPT_EXPORT_STRUCT(TestD);
M_TEST_DEFINE(testALayout) {
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls;
@ -492,9 +523,331 @@ M_TEST_DEFINE(testADynamic) {
assert_false(cls->init);
}
M_TEST_DEFINE(testBLayout) {
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestB)->details.cls;
assert_false(cls->init);
mScriptClassInit(cls);
assert_true(cls->init);
struct mScriptClassMember* member;
// Instance members
member = HashTableLookup(&cls->instanceMembers, "i");
assert_non_null(member);
assert_string_equal(member->name, "i");
assert_string_equal(member->docstring, MEMBER_A_DOCSTRING);
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32);
assert_int_equal(member->offset, 0);
member = HashTableLookup(&cls->instanceMembers, "i2");
assert_non_null(member);
assert_string_equal(member->name, "i2");
assert_null(member->docstring);
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32);
assert_int_equal(member->offset, sizeof(int32_t));
member = HashTableLookup(&cls->instanceMembers, "b8");
assert_non_null(member);
assert_string_equal(member->name, "b8");
assert_null(member->docstring);
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S8);
assert_int_equal(member->offset, sizeof(int32_t) * 2);
member = HashTableLookup(&cls->instanceMembers, "hUnaligned");
assert_non_null(member);
assert_string_equal(member->name, "hUnaligned");
assert_null(member->docstring);
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S16);
assert_int_not_equal(member->offset, sizeof(int32_t) * 2 + 1);
size_t hOffset = member->offset;
member = HashTableLookup(&cls->instanceMembers, "i3");
assert_non_null(member);
assert_string_equal(member->name, "i3");
assert_null(member->docstring);
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32);
assert_true(member->offset >= hOffset + sizeof(int16_t));
member = HashTableLookup(&cls->instanceMembers, "_super");
assert_non_null(member);
assert_string_equal(member->name, "_super");
assert_null(member->docstring);
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S(TestA));
assert_int_equal(member->offset, 0);
member = HashTableLookup(&cls->instanceMembers, "unknown");
assert_null(member);
mScriptClassDeinit(cls);
assert_false(cls->init);
}
M_TEST_DEFINE(testBGet) {
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestB)->details.cls;
struct TestB s = {
.d = {
.i = 1,
.i2 = 2,
.b8 = 3,
.hUnaligned = 4
},
.i3 = 5
};
struct mScriptValue sval = mSCRIPT_MAKE_S(TestB, &s);
struct mScriptValue super;
struct mScriptValue val;
struct mScriptValue compare;
compare = mSCRIPT_MAKE_S32(1);
assert_true(mScriptObjectGet(&sval, "i", &val));
assert_true(compare.type->equal(&compare, &val));
compare = mSCRIPT_MAKE_S32(2);
assert_true(mScriptObjectGet(&sval, "i2", &val));
assert_true(compare.type->equal(&compare, &val));
compare = mSCRIPT_MAKE_S32(3);
assert_true(mScriptObjectGet(&sval, "b8", &val));
assert_true(compare.type->equal(&compare, &val));
compare = mSCRIPT_MAKE_S32(4);
assert_true(mScriptObjectGet(&sval, "hUnaligned", &val));
assert_true(compare.type->equal(&compare, &val));
compare = mSCRIPT_MAKE_S32(5);
assert_true(mScriptObjectGet(&sval, "i3", &val));
assert_true(compare.type->equal(&compare, &val));
// Superclass explicit access
assert_true(mScriptObjectGet(&sval, "_super", &super));
assert_true(super.type == mSCRIPT_TYPE_MS_S(TestA));
compare = mSCRIPT_MAKE_S32(1);
assert_true(mScriptObjectGet(&super, "i", &val));
assert_true(compare.type->equal(&compare, &val));
compare = mSCRIPT_MAKE_S32(2);
assert_true(mScriptObjectGet(&super, "i2", &val));
assert_true(compare.type->equal(&compare, &val));
compare = mSCRIPT_MAKE_S32(3);
assert_true(mScriptObjectGet(&super, "b8", &val));
assert_true(compare.type->equal(&compare, &val));
compare = mSCRIPT_MAKE_S32(4);
assert_true(mScriptObjectGet(&super, "hUnaligned", &val));
assert_true(compare.type->equal(&compare, &val));
assert_false(mScriptObjectGet(&super, "i3", &val));
// Test const-correctness
sval = mSCRIPT_MAKE_CS(TestB, &s);
assert_true(mScriptObjectGet(&sval, "_super", &super));
assert_true(super.type == mSCRIPT_TYPE_MS_CS(TestA));
assert_true(cls->init);
mScriptClassDeinit(cls);
assert_false(cls->init);
}
M_TEST_DEFINE(testBSet) {
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestB)->details.cls;
struct TestB s = {
.d = {
.i = 1,
.i2 = 2,
.b8 = 3,
.hUnaligned = 4
},
.i3 = 5
};
struct mScriptValue sval = mSCRIPT_MAKE_S(TestB, &s);
struct mScriptValue super;
struct mScriptValue val;
val = mSCRIPT_MAKE_S32(2);
assert_true(mScriptObjectSet(&sval, "i", &val));
assert_int_equal(s.d.i, 2);
val = mSCRIPT_MAKE_S32(3);
assert_true(mScriptObjectSet(&sval, "i2", &val));
assert_int_equal(s.d.i2, 3);
val = mSCRIPT_MAKE_S32(4);
assert_true(mScriptObjectSet(&sval, "b8", &val));
assert_int_equal(s.d.b8, 4);
val = mSCRIPT_MAKE_S32(5);
assert_true(mScriptObjectSet(&sval, "hUnaligned", &val));
assert_int_equal(s.d.hUnaligned, 5);
val = mSCRIPT_MAKE_S32(6);
assert_true(mScriptObjectSet(&sval, "i3", &val));
assert_int_equal(s.i3, 6);
// Superclass explicit access
assert_true(mScriptObjectGet(&sval, "_super", &super));
assert_true(super.type == mSCRIPT_TYPE_MS_S(TestA));
val = mSCRIPT_MAKE_S32(3);
assert_true(mScriptObjectSet(&super, "i", &val));
assert_int_equal(s.d.i, 3);
val = mSCRIPT_MAKE_S32(4);
assert_true(mScriptObjectSet(&super, "i2", &val));
assert_int_equal(s.d.i2, 4);
val = mSCRIPT_MAKE_S32(5);
assert_true(mScriptObjectSet(&super, "b8", &val));
assert_int_equal(s.d.b8, 5);
val = mSCRIPT_MAKE_S32(6);
assert_true(mScriptObjectSet(&super, "hUnaligned", &val));
assert_int_equal(s.d.hUnaligned, 6);
val = mSCRIPT_MAKE_S32(7);
assert_false(mScriptObjectSet(&super, "i3", &val));
assert_int_equal(s.i3, 6);
// Const access
sval = mSCRIPT_MAKE_CS(TestB, &s);
val = mSCRIPT_MAKE_S32(4);
assert_false(mScriptObjectSet(&sval, "i", &val));
assert_int_equal(s.d.i, 3);
val = mSCRIPT_MAKE_S32(5);
assert_false(mScriptObjectSet(&sval, "i2", &val));
assert_int_equal(s.d.i2, 4);
val = mSCRIPT_MAKE_S32(6);
assert_false(mScriptObjectSet(&sval, "b8", &val));
assert_int_equal(s.d.b8, 5);
val = mSCRIPT_MAKE_S32(7);
assert_false(mScriptObjectSet(&sval, "hUnaligned", &val));
assert_int_equal(s.d.hUnaligned, 6);
val = mSCRIPT_MAKE_S32(8);
assert_false(mScriptObjectSet(&sval, "i3", &val));
assert_int_equal(s.i3, 6);
assert_true(cls->init);
mScriptClassDeinit(cls);
assert_false(cls->init);
}
M_TEST_DEFINE(testDLayout) {
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestD)->details.cls;
assert_false(cls->init);
mScriptClassInit(cls);
assert_true(cls->init);
struct mScriptClassMember* member;
// Instance members
member = HashTableLookup(&cls->instanceMembers, "a");
assert_non_null(member);
assert_string_equal(member->name, "a");
assert_null(member->docstring);
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S(TestC));
assert_int_equal(member->offset, 0);
member = HashTableLookup(&cls->instanceMembers, "b");
assert_non_null(member);
assert_string_equal(member->name, "b");
assert_null(member->docstring);
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S(TestC));
assert_int_equal(member->offset, sizeof(struct TestC));
mScriptClassDeinit(cls);
assert_false(cls->init);
}
M_TEST_DEFINE(testDGet) {
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestD)->details.cls;
struct TestD s = {
.a = { 1 },
.b = { 2 },
};
struct mScriptValue sval = mSCRIPT_MAKE_S(TestD, &s);
struct mScriptValue val;
struct mScriptValue member;
struct mScriptValue compare;
compare = mSCRIPT_MAKE_S32(1);
assert_true(mScriptObjectGet(&sval, "a", &member));
assert_true(mScriptObjectGet(&member, "i", &val));
assert_true(compare.type->equal(&compare, &val));
compare = mSCRIPT_MAKE_S32(2);
assert_true(mScriptObjectGet(&sval, "b", &member));
assert_true(mScriptObjectGet(&member, "i", &val));
assert_true(compare.type->equal(&compare, &val));
assert_true(cls->init);
mScriptClassDeinit(cls);
assert_false(cls->init);
}
M_TEST_DEFINE(testDSet) {
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestD)->details.cls;
struct TestD s = {
.a = { 1 },
.b = { 2 },
};
struct mScriptValue sval = mSCRIPT_MAKE_S(TestD, &s);
struct mScriptValue member;
struct mScriptValue val;
val = mSCRIPT_MAKE_S32(2);
assert_true(mScriptObjectGet(&sval, "a", &member));
assert_true(mScriptObjectSet(&member, "i", &val));
assert_int_equal(s.a.i, 2);
assert_int_equal(s.b.i, 2);
val = mSCRIPT_MAKE_S32(3);
assert_true(mScriptObjectGet(&sval, "b", &member));
assert_true(mScriptObjectSet(&member, "i", &val));
assert_int_equal(s.a.i, 2);
assert_int_equal(s.b.i, 3);
sval = mSCRIPT_MAKE_CS(TestD, &s);
val = mSCRIPT_MAKE_S32(4);
assert_true(mScriptObjectGet(&sval, "a", &member));
assert_false(mScriptObjectSet(&member, "i", &val));
assert_int_equal(s.a.i, 2);
assert_int_equal(s.b.i, 3);
val = mSCRIPT_MAKE_S32(5);
assert_true(mScriptObjectGet(&sval, "b", &member));
assert_false(mScriptObjectSet(&member, "i", &val));
assert_int_equal(s.a.i, 2);
assert_int_equal(s.b.i, 3);
assert_true(cls->init);
mScriptClassDeinit(cls);
assert_false(cls->init);
}
M_TEST_SUITE_DEFINE(mScriptClasses,
cmocka_unit_test(testALayout),
cmocka_unit_test(testAGet),
cmocka_unit_test(testASet),
cmocka_unit_test(testAStatic),
cmocka_unit_test(testADynamic))
cmocka_unit_test(testADynamic),
cmocka_unit_test(testBLayout),
cmocka_unit_test(testBGet),
cmocka_unit_test(testBSet),
cmocka_unit_test(testDLayout),
cmocka_unit_test(testDGet),
cmocka_unit_test(testDSet))

View File

@ -741,33 +741,34 @@ void mScriptFrameDeinit(struct mScriptFrame* frame) {
mScriptListDeinit(&frame->arguments);
}
void mScriptClassInit(struct mScriptTypeClass* cls) {
if (cls->init) {
return;
}
HashTableInit(&cls->staticMembers, 0, free);
HashTableInit(&cls->instanceMembers, 0, free);
size_t staticOffset = 0;
static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScriptClassInitDetails* details, bool child) {
const char* docstring = NULL;
size_t staticOffset = 0;
size_t i;
for (i = 0; cls->details[i].type != mSCRIPT_CLASS_INIT_END; ++i) {
const struct mScriptClassInitDetails* details = &cls->details[i];
for (i = 0; details[i].type != mSCRIPT_CLASS_INIT_END; ++i) {
const struct mScriptClassInitDetails* detail = &details[i];
struct mScriptClassMember* member;
switch (details->type) {
switch (detail->type) {
case mSCRIPT_CLASS_INIT_END:
break;
case mSCRIPT_CLASS_INIT_DOCSTRING:
docstring = details->info.comment;
docstring = detail->info.comment;
break;
case mSCRIPT_CLASS_INIT_INHERIT:
// TODO
abort();
member = calloc(1, sizeof(*member));
member->name = "_super";
member->type = detail->info.parent;
if (!child) {
cls->parent = detail->info.parent;
}
HashTableInsert(&cls->instanceMembers, member->name, member);
_mScriptClassInit(cls, detail->info.parent->details.cls->details, true);
break;
case mSCRIPT_CLASS_INIT_INSTANCE_MEMBER:
member = calloc(1, sizeof(*member));
memcpy(member, &details->info.member, sizeof(*member));
memcpy(member, &detail->info.member, sizeof(*member));
if (docstring) {
member->docstring = docstring;
docstring = NULL;
@ -775,27 +776,40 @@ void mScriptClassInit(struct mScriptTypeClass* cls) {
HashTableInsert(&cls->instanceMembers, member->name, member);
break;
case mSCRIPT_CLASS_INIT_STATIC_MEMBER:
member = calloc(1, sizeof(*member));
memcpy(member, &details->info.member, sizeof(*member));
if (docstring) {
member->docstring = docstring;
docstring = NULL;
}
// Alignment check
if (staticOffset & (details->info.member.type->size - 1)) {
size_t size = details->info.member.type->size;
if (size > MAX_ALIGNMENT) {
size = MAX_ALIGNMENT;
if (!child) {
member = calloc(1, sizeof(*member));
memcpy(member, &detail->info.member, sizeof(*member));
if (docstring) {
member->docstring = docstring;
docstring = NULL;
}
staticOffset = (staticOffset & ~(size - 1)) + size;
// Alignment check
if (staticOffset & (detail->info.member.type->size - 1)) {
size_t size = detail->info.member.type->size;
if (size > MAX_ALIGNMENT) {
size = MAX_ALIGNMENT;
}
staticOffset = (staticOffset & ~(size - 1)) + size;
}
member->offset = staticOffset;
staticOffset += detail->info.member.type->size;
HashTableInsert(&cls->staticMembers, member->name, member);
}
member->offset = staticOffset;
staticOffset += details->info.member.type->size;
HashTableInsert(&cls->staticMembers, member->name, member);
break;
}
}
}
void mScriptClassInit(struct mScriptTypeClass* cls) {
if (cls->init) {
return;
}
HashTableInit(&cls->staticMembers, 0, free);
HashTableInit(&cls->instanceMembers, 0, free);
_mScriptClassInit(cls, cls->details, false);
cls->init = true;
}
@ -880,6 +894,15 @@ bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScri
val->type = m->type;
m->type->alloc(val);
break;
case mSCRIPT_TYPE_OBJECT:
val->refs = mSCRIPT_VALUE_UNREF;
val->value.opaque = rawMember;
if (obj->type->isConst && !m->type->isConst) {
val->type = m->type->constType;
} else {
val->type = m->type;
}
break;
default:
return false;
}