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

View File

@ -21,6 +21,20 @@ struct TestA {
int32_t (*icfn1)(const struct TestA*, int); 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) { static int32_t testAi0(struct TestA* a) {
return a->i; return a->i;
} }
@ -87,7 +101,24 @@ mSCRIPT_DEFINE_STRUCT(TestA)
mSCRIPT_DEFINE_STATIC_MEMBER(S16, s_hUnaligned) mSCRIPT_DEFINE_STATIC_MEMBER(S16, s_hUnaligned)
mSCRIPT_DEFINE_END; 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(TestA);
mSCRIPT_EXPORT_STRUCT(TestB);
mSCRIPT_EXPORT_STRUCT(TestC);
mSCRIPT_EXPORT_STRUCT(TestD);
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;
@ -492,9 +523,331 @@ M_TEST_DEFINE(testADynamic) {
assert_false(cls->init); 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, M_TEST_SUITE_DEFINE(mScriptClasses,
cmocka_unit_test(testALayout), cmocka_unit_test(testALayout),
cmocka_unit_test(testAGet), cmocka_unit_test(testAGet),
cmocka_unit_test(testASet), cmocka_unit_test(testASet),
cmocka_unit_test(testAStatic), 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); mScriptListDeinit(&frame->arguments);
} }
void mScriptClassInit(struct mScriptTypeClass* cls) { static void _mScriptClassInit(struct mScriptTypeClass* cls, const struct mScriptClassInitDetails* details, bool child) {
if (cls->init) {
return;
}
HashTableInit(&cls->staticMembers, 0, free);
HashTableInit(&cls->instanceMembers, 0, free);
size_t staticOffset = 0;
const char* docstring = NULL; const char* docstring = NULL;
size_t staticOffset = 0;
size_t i; size_t i;
for (i = 0; cls->details[i].type != mSCRIPT_CLASS_INIT_END; ++i) { for (i = 0; details[i].type != mSCRIPT_CLASS_INIT_END; ++i) {
const struct mScriptClassInitDetails* details = &cls->details[i]; const struct mScriptClassInitDetails* detail = &details[i];
struct mScriptClassMember* member; struct mScriptClassMember* member;
switch (details->type) { switch (detail->type) {
case mSCRIPT_CLASS_INIT_END: case mSCRIPT_CLASS_INIT_END:
break; break;
case mSCRIPT_CLASS_INIT_DOCSTRING: case mSCRIPT_CLASS_INIT_DOCSTRING:
docstring = details->info.comment; docstring = detail->info.comment;
break; break;
case mSCRIPT_CLASS_INIT_INHERIT: case mSCRIPT_CLASS_INIT_INHERIT:
// TODO member = calloc(1, sizeof(*member));
abort(); 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; break;
case mSCRIPT_CLASS_INIT_INSTANCE_MEMBER: case mSCRIPT_CLASS_INIT_INSTANCE_MEMBER:
member = calloc(1, sizeof(*member)); member = calloc(1, sizeof(*member));
memcpy(member, &details->info.member, sizeof(*member)); memcpy(member, &detail->info.member, sizeof(*member));
if (docstring) { if (docstring) {
member->docstring = docstring; member->docstring = docstring;
docstring = NULL; docstring = NULL;
@ -775,27 +776,40 @@ void mScriptClassInit(struct mScriptTypeClass* cls) {
HashTableInsert(&cls->instanceMembers, member->name, member); HashTableInsert(&cls->instanceMembers, member->name, member);
break; break;
case mSCRIPT_CLASS_INIT_STATIC_MEMBER: case mSCRIPT_CLASS_INIT_STATIC_MEMBER:
member = calloc(1, sizeof(*member)); if (!child) {
memcpy(member, &details->info.member, sizeof(*member)); member = calloc(1, sizeof(*member));
if (docstring) { memcpy(member, &detail->info.member, sizeof(*member));
member->docstring = docstring; if (docstring) {
docstring = NULL; 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;
} }
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; 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; cls->init = true;
} }
@ -880,6 +894,15 @@ bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScri
val->type = m->type; val->type = m->type;
m->type->alloc(val); m->type->alloc(val);
break; 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: default:
return false; return false;
} }