mirror of https://github.com/mgba-emu/mgba.git
Scripting: First pass on structs/classes
This commit is contained in:
parent
c8848876fa
commit
af2e226cc4
|
@ -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);
|
||||
|
|
|
@ -4,6 +4,7 @@ set(SOURCE_FILES
|
|||
types.c)
|
||||
|
||||
set(TEST_FILES
|
||||
test/classes.c
|
||||
test/types.c)
|
||||
|
||||
if(USE_LUA)
|
||||
|
|
|
@ -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))
|
|
@ -12,6 +12,9 @@ struct Test {
|
|||
int32_t a;
|
||||
};
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(Test)
|
||||
mSCRIPT_DEFINE_END
|
||||
|
||||
mSCRIPT_EXPORT_STRUCT(Test);
|
||||
|
||||
static int voidOne(void) {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue