From af2e226cc4bac1a5db2c31fb3318508312ffa197 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 27 Apr 2022 00:30:49 -0700 Subject: [PATCH] Scripting: First pass on structs/classes --- include/mgba/script/types.h | 75 ++++++++++++++++++++++++ src/script/CMakeLists.txt | 1 + src/script/test/classes.c | 113 ++++++++++++++++++++++++++++++++++++ src/script/test/types.c | 3 + src/script/types.c | 69 ++++++++++++++++++++++ 5 files changed, 261 insertions(+) create mode 100644 src/script/test/classes.c diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 1ddaffb55..58ba0b0fe 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -10,6 +10,7 @@ CXX_GUARD_START +#include #include #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); diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index ff4e4146f..aa7cc1562 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -4,6 +4,7 @@ set(SOURCE_FILES types.c) set(TEST_FILES + test/classes.c test/types.c) if(USE_LUA) diff --git a/src/script/test/classes.c b/src/script/test/classes.c new file mode 100644 index 000000000..1135342cb --- /dev/null +++ b/src/script/test/classes.c @@ -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 +#include + +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)) diff --git a/src/script/test/types.c b/src/script/test/types.c index caeb8cd05..b3806bfa6 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -12,6 +12,9 @@ struct Test { int32_t a; }; +mSCRIPT_DEFINE_STRUCT(Test) +mSCRIPT_DEFINE_END + mSCRIPT_EXPORT_STRUCT(Test); static int voidOne(void) { diff --git a/src/script/types.c b/src/script/types.c index cba832563..bc2ffcae0 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -8,6 +8,8 @@ #include #include +#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;