Scripting: First pass on structs/classes

This commit is contained in:
Vicki Pfau 2022-04-27 00:30:49 -07:00
parent c8848876fa
commit af2e226cc4
5 changed files with 261 additions and 0 deletions

View File

@ -10,6 +10,7 @@
CXX_GUARD_START
#include <mgba-util/table.h>
#include <mgba-util/vector.h>
#define _mAPPLY(...) __VA_ARGS__
@ -151,12 +152,51 @@ CXX_GUARD_START
#define mSCRIPT_EXPORT_STRUCT(STRUCT) \
const struct mScriptType mSTStruct_ ## STRUCT = { \
.base = mSCRIPT_TYPE_OBJECT, \
.details = { \
.cls = &_mSTStructDetails_ ## STRUCT \
}, \
.size = sizeof(struct STRUCT), \
.name = "struct::" #STRUCT, \
.alloc = NULL, \
.free = NULL, \
}
#define mSCRIPT_DECLARE_STRUCT(STRUCT) extern const struct mScriptType mSTStruct_ ## STRUCT;
#define mSCRIPT_DEFINE_STRUCT(STRUCT) \
static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT = { \
.init = false, \
.details = (const struct mScriptClassInitDetails[]) {
#define mSCRIPT_DEFINE_DOCSTRING(DOCSTRING) { \
.type = mSCRIPT_CLASS_INIT_DOCSTRING, \
.info = { \
.comment = DOCSTRING \
} \
},
#define mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, NAME) { \
.type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \
.info = { \
.member = { \
.name = #NAME, \
.type = _mAPPLY(mSCRIPT_TYPE_MS_ ## TYPE), \
.offset = offsetof(STRUCT, NAME) \
} \
} \
},
#define mSCRIPT_DEFINE_STATIC_MEMBER(TYPE, NAME) { \
.type = mSCRIPT_CLASS_INIT_STATIC_MEMBER, \
.info = { \
.member = { \
.name = #NAME, \
.type = _mAPPLY(mSCRIPT_TYPE_MS_ ## TYPE) \
} \
}, \
},
#define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } };
#define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \
static const struct mScriptType _type_ ## NAME = { \
.base = mSCRIPT_TYPE_FUNCTION, \
@ -243,6 +283,14 @@ enum {
mSCRIPT_TYPE_WRAPPER
};
enum mScriptClassInitType {
mSCRIPT_CLASS_INIT_END = 0,
mSCRIPT_CLASS_INIT_DOCSTRING,
mSCRIPT_CLASS_INIT_INSTANCE_MEMBER,
mSCRIPT_CLASS_INIT_STATIC_MEMBER,
mSCRIPT_CLASS_INIT_INHERIT,
};
struct Table;
struct mScriptType;
extern const struct mScriptType mSTVoid;
@ -273,6 +321,29 @@ struct mScriptTypeFunction {
// TODO: kwargs, defaults
};
struct mScriptClassMember {
const char* name;
const char* docstring;
const struct mScriptType* type;
size_t offset;
};
struct mScriptClassInitDetails {
enum mScriptClassInitType type;
union {
const char* comment;
const struct mScriptTypeClass* parent;
struct mScriptClassMember member;
} info;
};
struct mScriptTypeClass {
bool init;
const struct mScriptClassInitDetails* details;
struct Table staticMembers;
struct Table instanceMembers;
};
struct mScriptValue;
struct mScriptType {
int base;
@ -281,6 +352,7 @@ struct mScriptType {
union {
struct mScriptTypeTuple tuple;
struct mScriptTypeFunction function;
struct mScriptTypeClass* cls;
void* opaque;
} details;
void (*alloc)(struct mScriptValue*);
@ -339,6 +411,9 @@ struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScri
void mScriptFrameInit(struct mScriptFrame* frame);
void mScriptFrameDeinit(struct mScriptFrame* frame);
void mScriptClassInit(struct mScriptTypeClass* cls);
void mScriptClassDeinit(struct mScriptTypeClass* cls);
bool mScriptPopS32(struct mScriptList* list, int32_t* out);
bool mScriptPopU32(struct mScriptList* list, uint32_t* out);
bool mScriptPopF32(struct mScriptList* list, float* out);

View File

@ -4,6 +4,7 @@ set(SOURCE_FILES
types.c)
set(TEST_FILES
test/classes.c
test/types.c)
if(USE_LUA)

113
src/script/test/classes.c Normal file
View File

@ -0,0 +1,113 @@
/* Copyright (c) 2013-2022 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "util/test/suite.h"
#include <mgba/script/context.h>
#include <mgba/script/types.h>
struct TestA {
int32_t i;
int32_t i2;
int8_t b8;
int16_t hUnaligned;
};
#define MEMBER_A_DOCSTRING "Member a"
mSCRIPT_DEFINE_STRUCT(TestA)
mSCRIPT_DEFINE_DOCSTRING(MEMBER_A_DOCSTRING)
mSCRIPT_DEFINE_STRUCT_MEMBER(struct TestA, S32, i)
mSCRIPT_DEFINE_STRUCT_MEMBER(struct TestA, S32, i2)
mSCRIPT_DEFINE_STRUCT_MEMBER(struct TestA, S8, b8)
mSCRIPT_DEFINE_STRUCT_MEMBER(struct TestA, S16, hUnaligned)
mSCRIPT_DEFINE_DOCSTRING(MEMBER_A_DOCSTRING)
mSCRIPT_DEFINE_STATIC_MEMBER(S32, i)
mSCRIPT_DEFINE_STATIC_MEMBER(S32, i2)
mSCRIPT_DEFINE_STATIC_MEMBER(S8, b8)
mSCRIPT_DEFINE_STATIC_MEMBER(S16, hUnaligned)
mSCRIPT_DEFINE_END
mSCRIPT_EXPORT_STRUCT(TestA);
M_TEST_DEFINE(testALayout) {
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->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);
member = HashTableLookup(&cls->instanceMembers, "unknown");
assert_null(member);
// Static members
member = HashTableLookup(&cls->staticMembers, "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->staticMembers, "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->staticMembers, "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->staticMembers, "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);
member = HashTableLookup(&cls->staticMembers, "unknown");
assert_null(member);
mScriptClassDeinit(cls);
assert_false(cls->init);
}
M_TEST_SUITE_DEFINE(mScriptClasses,
cmocka_unit_test(testALayout))

View File

@ -12,6 +12,9 @@ struct Test {
int32_t a;
};
mSCRIPT_DEFINE_STRUCT(Test)
mSCRIPT_DEFINE_END
mSCRIPT_EXPORT_STRUCT(Test);
static int voidOne(void) {

View File

@ -8,6 +8,8 @@
#include <mgba-util/hash.h>
#include <mgba-util/table.h>
#define MAX_ALIGNMENT 8
static void _allocTable(struct mScriptValue*);
static void _freeTable(struct mScriptValue*);
static void _deinitTableValue(void*);
@ -738,6 +740,73 @@ 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;
const char* docstring = NULL;
size_t i;
for (i = 0; cls->details[i].type != mSCRIPT_CLASS_INIT_END; ++i) {
const struct mScriptClassInitDetails* details = &cls->details[i];
struct mScriptClassMember* member;
switch (details->type) {
case mSCRIPT_CLASS_INIT_END:
break;
case mSCRIPT_CLASS_INIT_DOCSTRING:
docstring = details->info.comment;
break;
case mSCRIPT_CLASS_INIT_INHERIT:
// TODO
abort();
break;
case mSCRIPT_CLASS_INIT_INSTANCE_MEMBER:
member = calloc(1, sizeof(*member));
memcpy(member, &details->info.member, sizeof(*member));
if (docstring) {
member->docstring = docstring;
docstring = NULL;
}
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;
}
staticOffset = (staticOffset & ~(size - 1)) + size;
}
member->offset = staticOffset;
staticOffset += details->info.member.type->size;
HashTableInsert(&cls->staticMembers, member->name, member);
break;
}
}
cls->init = true;
}
void mScriptClassDeinit(struct mScriptTypeClass* cls) {
if (!cls->init) {
return;
}
HashTableDeinit(&cls->instanceMembers);
HashTableDeinit(&cls->staticMembers);
cls->init = false;
}
bool mScriptPopS32(struct mScriptList* list, int32_t* out) {
mSCRIPT_POP(list, S32, val);
*out = val;