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
|
CXX_GUARD_START
|
||||||
|
|
||||||
|
#include <mgba-util/table.h>
|
||||||
#include <mgba-util/vector.h>
|
#include <mgba-util/vector.h>
|
||||||
|
|
||||||
#define _mAPPLY(...) __VA_ARGS__
|
#define _mAPPLY(...) __VA_ARGS__
|
||||||
|
@ -151,12 +152,51 @@ CXX_GUARD_START
|
||||||
#define mSCRIPT_EXPORT_STRUCT(STRUCT) \
|
#define mSCRIPT_EXPORT_STRUCT(STRUCT) \
|
||||||
const struct mScriptType mSTStruct_ ## STRUCT = { \
|
const struct mScriptType mSTStruct_ ## STRUCT = { \
|
||||||
.base = mSCRIPT_TYPE_OBJECT, \
|
.base = mSCRIPT_TYPE_OBJECT, \
|
||||||
|
.details = { \
|
||||||
|
.cls = &_mSTStructDetails_ ## STRUCT \
|
||||||
|
}, \
|
||||||
.size = sizeof(struct STRUCT), \
|
.size = sizeof(struct STRUCT), \
|
||||||
.name = "struct::" #STRUCT, \
|
.name = "struct::" #STRUCT, \
|
||||||
.alloc = NULL, \
|
.alloc = NULL, \
|
||||||
.free = 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, ...) \
|
#define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \
|
||||||
static const struct mScriptType _type_ ## NAME = { \
|
static const struct mScriptType _type_ ## NAME = { \
|
||||||
.base = mSCRIPT_TYPE_FUNCTION, \
|
.base = mSCRIPT_TYPE_FUNCTION, \
|
||||||
|
@ -243,6 +283,14 @@ enum {
|
||||||
mSCRIPT_TYPE_WRAPPER
|
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 Table;
|
||||||
struct mScriptType;
|
struct mScriptType;
|
||||||
extern const struct mScriptType mSTVoid;
|
extern const struct mScriptType mSTVoid;
|
||||||
|
@ -273,6 +321,29 @@ struct mScriptTypeFunction {
|
||||||
// TODO: kwargs, defaults
|
// 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 mScriptValue;
|
||||||
struct mScriptType {
|
struct mScriptType {
|
||||||
int base;
|
int base;
|
||||||
|
@ -281,6 +352,7 @@ struct mScriptType {
|
||||||
union {
|
union {
|
||||||
struct mScriptTypeTuple tuple;
|
struct mScriptTypeTuple tuple;
|
||||||
struct mScriptTypeFunction function;
|
struct mScriptTypeFunction function;
|
||||||
|
struct mScriptTypeClass* cls;
|
||||||
void* opaque;
|
void* opaque;
|
||||||
} details;
|
} details;
|
||||||
void (*alloc)(struct mScriptValue*);
|
void (*alloc)(struct mScriptValue*);
|
||||||
|
@ -339,6 +411,9 @@ struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScri
|
||||||
void mScriptFrameInit(struct mScriptFrame* frame);
|
void mScriptFrameInit(struct mScriptFrame* frame);
|
||||||
void mScriptFrameDeinit(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 mScriptPopS32(struct mScriptList* list, int32_t* out);
|
||||||
bool mScriptPopU32(struct mScriptList* list, uint32_t* out);
|
bool mScriptPopU32(struct mScriptList* list, uint32_t* out);
|
||||||
bool mScriptPopF32(struct mScriptList* list, float* out);
|
bool mScriptPopF32(struct mScriptList* list, float* out);
|
||||||
|
|
|
@ -4,6 +4,7 @@ set(SOURCE_FILES
|
||||||
types.c)
|
types.c)
|
||||||
|
|
||||||
set(TEST_FILES
|
set(TEST_FILES
|
||||||
|
test/classes.c
|
||||||
test/types.c)
|
test/types.c)
|
||||||
|
|
||||||
if(USE_LUA)
|
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;
|
int32_t a;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mSCRIPT_DEFINE_STRUCT(Test)
|
||||||
|
mSCRIPT_DEFINE_END
|
||||||
|
|
||||||
mSCRIPT_EXPORT_STRUCT(Test);
|
mSCRIPT_EXPORT_STRUCT(Test);
|
||||||
|
|
||||||
static int voidOne(void) {
|
static int voidOne(void) {
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include <mgba-util/hash.h>
|
#include <mgba-util/hash.h>
|
||||||
#include <mgba-util/table.h>
|
#include <mgba-util/table.h>
|
||||||
|
|
||||||
|
#define MAX_ALIGNMENT 8
|
||||||
|
|
||||||
static void _allocTable(struct mScriptValue*);
|
static void _allocTable(struct mScriptValue*);
|
||||||
static void _freeTable(struct mScriptValue*);
|
static void _freeTable(struct mScriptValue*);
|
||||||
static void _deinitTableValue(void*);
|
static void _deinitTableValue(void*);
|
||||||
|
@ -738,6 +740,73 @@ void mScriptFrameDeinit(struct mScriptFrame* frame) {
|
||||||
mScriptListDeinit(&frame->arguments);
|
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) {
|
bool mScriptPopS32(struct mScriptList* list, int32_t* out) {
|
||||||
mSCRIPT_POP(list, S32, val);
|
mSCRIPT_POP(list, S32, val);
|
||||||
*out = val;
|
*out = val;
|
||||||
|
|
Loading…
Reference in New Issue