diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index 8633e0ed4..780067d83 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -14,8 +14,9 @@ CXX_GUARD_START #include #include +#define mSCRIPT_KV_PAIR(KEY, VALUE) { #KEY, VALUE } #define mSCRIPT_CONSTANT_PAIR(NS, CONST) { #CONST, mScriptValueCreateFromSInt(NS ## _ ## CONST) } -#define mSCRIPT_CONSTANT_SENTINEL { NULL, NULL } +#define mSCRIPT_KV_SENTINEL { NULL, NULL } struct mScriptFrame; struct mScriptFunction; @@ -80,6 +81,7 @@ void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref); void mScriptContextAttachStdlib(struct mScriptContext* context); void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants); +void mScriptContextExportNamespace(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* value); void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback); void mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value); diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 489b5eee4..471d78524 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -41,6 +41,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_C_PS(X) void #define mSCRIPT_TYPE_C_PCS(X) void #define mSCRIPT_TYPE_C_WSTR struct mScriptValue* +#define mSCRIPT_TYPE_C_WLIST struct mScriptValue* #define mSCRIPT_TYPE_C_W(X) struct mScriptValue* #define mSCRIPT_TYPE_C_CW(X) const struct mScriptValue* @@ -67,6 +68,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_PS(STRUCT) opaque #define mSCRIPT_TYPE_FIELD_PCS(STRUCT) copaque #define mSCRIPT_TYPE_FIELD_WSTR opaque +#define mSCRIPT_TYPE_FIELD_WLIST opaque #define mSCRIPT_TYPE_FIELD_W(TYPE) opaque #define mSCRIPT_TYPE_FIELD_CW(TYPE) opaque @@ -92,6 +94,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_PS(STRUCT) (&mSTStructPtr_ ## STRUCT) #define mSCRIPT_TYPE_MS_PCS(STRUCT) (&mSTStructConstPtr_ ## STRUCT) #define mSCRIPT_TYPE_MS_WSTR (&mSTStringWrapper) +#define mSCRIPT_TYPE_MS_WLIST (&mSTListWrapper) #define mSCRIPT_TYPE_MS_W(TYPE) (&mSTWrapper_ ## TYPE) #define mSCRIPT_TYPE_MS_CW(TYPE) (&mSTWrapperConst_ ## TYPE) @@ -116,6 +119,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_CMP_S_METHOD(STRUCT, NAME) mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME)->name == _mSCRIPT_FIELD_NAME #define mSCRIPT_TYPE_CMP(TYPE0, TYPE1) mSCRIPT_TYPE_CMP_ ## TYPE0(TYPE1) #define mSCRIPT_TYPE_CMP_WSTR(TYPE) (mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_STR, TYPE) || mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE)) +#define mSCRIPT_TYPE_CMP_WLIST(TYPE) (mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_LIST, TYPE)) enum mScriptTypeBase { mSCRIPT_TYPE_VOID = 0, @@ -168,6 +172,7 @@ extern const struct mScriptType mSTTable; extern const struct mScriptType mSTWrapper; extern const struct mScriptType mSTWeakref; extern const struct mScriptType mSTStringWrapper; +extern const struct mScriptType mSTListWrapper; struct mScriptType; struct mScriptValue { diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index c579ccb9f..e9ecc8c60 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -10,7 +10,9 @@ set(TEST_FILES if(USE_LUA) list(APPEND SOURCE_FILES engines/lua.c) - list(APPEND TEST_FILES test/lua.c) + list(APPEND TEST_FILES + test/stdlib.c + test/lua.c) endif() source_group("Scripting" FILES ${SOURCE_FILES}) diff --git a/src/script/context.c b/src/script/context.c index f2437dc5b..43f962caf 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -237,6 +237,18 @@ void mScriptContextExportConstants(struct mScriptContext* context, const char* n mScriptValueDeref(table); } +void mScriptContextExportNamespace(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* values) { + struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); + size_t i; + for (i = 0; values[i].key; ++i) { + struct mScriptValue* key = mScriptStringCreateFromUTF8(values[i].key); + mScriptTableInsert(table, key, values[i].value); + mScriptValueDeref(key); + mScriptValueDeref(values[i].value); + } + mScriptContextSetGlobal(context, nspace, table); +} + bool mScriptContextLoadVF(struct mScriptContext* context, const char* name, struct VFile* vf) { struct mScriptFileInfo info = { .name = name, diff --git a/src/script/stdlib.c b/src/script/stdlib.c index 35941f6d8..32770a752 100644 --- a/src/script/stdlib.c +++ b/src/script/stdlib.c @@ -30,6 +30,38 @@ static void _mScriptCallbackAdd(struct mScriptCallbackManager* adapter, struct m mSCRIPT_DECLARE_STRUCT(mScriptCallbackManager); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackManager, add, _mScriptCallbackAdd, 2, STR, callback, WRAPPER, function); +static uint64_t mScriptMakeBitmask(struct mScriptList* list) { + size_t i; + uint64_t mask = 0; + for (i = 0; i < mScriptListSize(list); ++i) { + struct mScriptValue bit; + struct mScriptValue* value = mScriptListGetPointer(list, i); + if (value->type->base == mSCRIPT_TYPE_WRAPPER) { + value = mScriptValueUnwrap(value); + } + if (!mScriptCast(mSCRIPT_TYPE_MS_U64, value, &bit)) { + continue; + } + mask |= 1ULL << bit.value.u64; + } + return mask; +} + +static struct mScriptValue* mScriptExpandBitmask(uint64_t mask) { + struct mScriptValue* list = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST); + size_t i; + for (i = 0; mask; ++i, mask >>= 1) { + if (!(mask & 1)) { + continue; + } + *mScriptListAppend(list->value.list) = mSCRIPT_MAKE_U32(i); + } + return list; +} + +mSCRIPT_BIND_FUNCTION(mScriptMakeBitmask_Binding, U64, mScriptMakeBitmask, 1, LIST, bits); +mSCRIPT_BIND_FUNCTION(mScriptExpandBitmask_Binding, WLIST, mScriptExpandBitmask, 1, U64, mask); + mSCRIPT_DEFINE_STRUCT(mScriptCallbackManager) mSCRIPT_DEFINE_CLASS_DOCSTRING( "A global singleton object `callbacks` used for managing callbacks. The following callbacks are defined:\n\n" @@ -66,17 +98,17 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) { mSCRIPT_CONSTANT_PAIR(SAVESTATE, RTC), mSCRIPT_CONSTANT_PAIR(SAVESTATE, METADATA), mSCRIPT_CONSTANT_PAIR(SAVESTATE, ALL), - mSCRIPT_CONSTANT_SENTINEL + mSCRIPT_KV_SENTINEL }); mScriptContextExportConstants(context, "PLATFORM", (struct mScriptKVPair[]) { mSCRIPT_CONSTANT_PAIR(mPLATFORM, NONE), mSCRIPT_CONSTANT_PAIR(mPLATFORM, GBA), mSCRIPT_CONSTANT_PAIR(mPLATFORM, GB), - mSCRIPT_CONSTANT_SENTINEL + mSCRIPT_KV_SENTINEL }); mScriptContextExportConstants(context, "CHECKSUM", (struct mScriptKVPair[]) { mSCRIPT_CONSTANT_PAIR(mCHECKSUM, CRC32), - mSCRIPT_CONSTANT_SENTINEL + mSCRIPT_KV_SENTINEL }); #ifdef M_CORE_GBA mScriptContextExportConstants(context, "GBA_KEY", (struct mScriptKVPair[]) { @@ -90,7 +122,7 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) { mSCRIPT_CONSTANT_PAIR(GBA_KEY, DOWN), mSCRIPT_CONSTANT_PAIR(GBA_KEY, R), mSCRIPT_CONSTANT_PAIR(GBA_KEY, L), - mSCRIPT_CONSTANT_SENTINEL + mSCRIPT_KV_SENTINEL }); #endif #ifdef M_CORE_GB @@ -103,8 +135,14 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) { mSCRIPT_CONSTANT_PAIR(GB_KEY, LEFT), mSCRIPT_CONSTANT_PAIR(GB_KEY, UP), mSCRIPT_CONSTANT_PAIR(GB_KEY, DOWN), - mSCRIPT_CONSTANT_SENTINEL + mSCRIPT_KV_SENTINEL }); #endif mScriptContextSetGlobal(context, "C", context->constants); + + mScriptContextExportNamespace(context, "util", (struct mScriptKVPair[]) { + mSCRIPT_KV_PAIR(makeBitmask, &mScriptMakeBitmask_Binding), + mSCRIPT_KV_PAIR(expandBitmask, &mScriptExpandBitmask_Binding), + mSCRIPT_KV_SENTINEL + }); } diff --git a/src/script/test/stdlib.c b/src/script/test/stdlib.c new file mode 100644 index 000000000..c6e9fd3fe --- /dev/null +++ b/src/script/test/stdlib.c @@ -0,0 +1,91 @@ +/* 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 +#include + +#define SETUP_LUA \ + struct mScriptContext context; \ + mScriptContextInit(&context); \ + struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA); \ + mScriptContextAttachStdlib(&context) + +#define LOAD_PROGRAM(PROG) \ + do { \ + struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ + assert_true(lua->load(lua, NULL, vf)); \ + vf->close(vf); \ + } while(0) + +#define TEST_PROGRAM(PROG) \ + LOAD_PROGRAM(PROG); \ + assert_true(lua->run(lua)); \ + +#define TEST_VALUE(TYPE, NAME, VALUE) \ + do { \ + struct mScriptValue val = mSCRIPT_MAKE(TYPE, VALUE); \ + struct mScriptValue* global = lua->getGlobal(lua, NAME); \ + assert_non_null(global); \ + assert_true(global->type->equal(global, &val)); \ + mScriptValueDeref(global); \ + } while(0) + +M_TEST_SUITE_SETUP(mScriptStdlib) { + if (mSCRIPT_ENGINE_LUA->init) { + mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA); + } + return 0; +} + +M_TEST_SUITE_TEARDOWN(mScriptStdlib) { + if (mSCRIPT_ENGINE_LUA->deinit) { + mSCRIPT_ENGINE_LUA->deinit(mSCRIPT_ENGINE_LUA); + } + return 0; +} + +M_TEST_DEFINE(bitMask) { + SETUP_LUA; + + TEST_PROGRAM("assert(util)"); + TEST_PROGRAM("assert(util.makeBitmask)"); + TEST_PROGRAM("assert(util.makeBitmask{0} == 1)"); + TEST_PROGRAM("assert(util.makeBitmask{1} == 2)"); + TEST_PROGRAM("assert(util.makeBitmask{0, 1} == 3)"); + TEST_PROGRAM("assert(util.makeBitmask{1, 1} == 2)"); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(bitUnmask) { + SETUP_LUA; + + TEST_PROGRAM("assert(util)"); + TEST_PROGRAM("assert(util.expandBitmask)"); + TEST_PROGRAM("assert(#util.expandBitmask(0) == 0)"); + TEST_PROGRAM("assert(#util.expandBitmask(1) == 1)"); + TEST_PROGRAM("assert(util.expandBitmask(1)[1] == 0)"); + TEST_PROGRAM("assert(#util.expandBitmask(2) == 1)"); + TEST_PROGRAM("assert(util.expandBitmask(2)[1] == 1)"); + TEST_PROGRAM("assert(#util.expandBitmask(3) == 2)"); + TEST_PROGRAM("assert(util.expandBitmask(3)[1] == 0 or util.expandBitmask(3)[1] == 1)"); + TEST_PROGRAM("assert(util.expandBitmask(3)[2] == 0 or util.expandBitmask(3)[2] == 1)"); + TEST_PROGRAM("assert(#util.expandBitmask(6) == 2)"); + TEST_PROGRAM("assert(util.expandBitmask(6)[1] == 1 or util.expandBitmask(6)[1] == 2)"); + TEST_PROGRAM("assert(util.expandBitmask(6)[2] == 1 or util.expandBitmask(6)[2] == 2)"); + TEST_PROGRAM("assert(#util.expandBitmask(7) == 3)"); + TEST_PROGRAM("assert(#util.expandBitmask(11) == 3)"); + TEST_PROGRAM("assert(#util.expandBitmask(15) == 4)"); + + mScriptContextDeinit(&context); +} + +M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptStdlib, + cmocka_unit_test(bitMask), + cmocka_unit_test(bitUnmask), +) diff --git a/src/script/types.c b/src/script/types.c index 4ab6653e1..61fcadcd2 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -221,6 +221,15 @@ const struct mScriptType mSTStringWrapper = { .hash = NULL, }; +const struct mScriptType mSTListWrapper = { + .base = mSCRIPT_TYPE_WRAPPER, + .size = sizeof(struct mScriptValue), + .name = "wrapper list", + .alloc = NULL, + .free = NULL, + .hash = NULL, +}; + const struct mScriptType mSTWeakref = { .base = mSCRIPT_TYPE_WEAKREF, .size = sizeof(uint32_t),