From 5c67c3b6000288bd57a587b46d959ee99576d071 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 3 May 2022 20:44:56 -0700 Subject: [PATCH] Scripting: Lua method calling cleanup and testing --- include/mgba/script/types.h | 11 ++- src/script/engines/lua.c | 57 ++++++++--- src/script/test/lua.c | 186 +++++++++++++++++++++++++++++++++--- 3 files changed, 226 insertions(+), 28 deletions(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index f2e9a37a9..446157a83 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -366,8 +366,16 @@ CXX_GUARD_START #define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } } #define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \ + static struct mScriptFunction _function_ ## NAME = { \ + .call = _binding_ ## NAME \ + }; \ + static void _alloc_ ## NAME(struct mScriptValue* val) { \ + val->value.copaque = &_function_ ## NAME; \ + } \ static const struct mScriptType _type_ ## NAME = { \ .base = mSCRIPT_TYPE_FUNCTION, \ + .name = "function::" #NAME, \ + .alloc = _alloc_ ## NAME, \ .details = { \ .function = { \ .parameters = { \ @@ -381,9 +389,6 @@ CXX_GUARD_START }, \ } \ }; \ - static struct mScriptFunction _function_ ## NAME = { \ - .call = _binding_ ## NAME \ - }; \ const struct mScriptValue NAME = { \ .type = &_type_ ## NAME, \ .refs = mSCRIPT_VALUE_UNREF, \ diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index eb1db84a8..2d9eb5294 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -4,6 +4,7 @@ * 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 +#include #include static struct mScriptEngineContext* _luaCreate(struct mScriptEngine2*, struct mScriptContext*); @@ -79,6 +80,12 @@ static struct mScriptEngineLua { struct mScriptEngine2* const mSCRIPT_ENGINE_LUA = &_engineLua.d; +static const luaL_Reg _mSTStruct[] = { + { "__index", _luaGetObject }, + { "__newindex", _luaSetObject }, + { NULL, NULL } +}; + struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mScriptContext* context) { UNUSED(engine); struct mScriptEngineContextLua* luaContext = calloc(1, sizeof(*luaContext)); @@ -93,13 +100,14 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS luaContext->lua = luaL_newstate(); luaContext->func = -1; + luaL_openlibs(luaContext->lua); + luaL_newmetatable(luaContext->lua, "mSTStruct"); - lua_pushliteral(luaContext->lua, "__index"); - lua_pushcfunction(luaContext->lua, _luaGetObject); - lua_rawset(luaContext->lua, -3); - lua_pushliteral(luaContext->lua, "__newindex"); - lua_pushcfunction(luaContext->lua, _luaSetObject); - lua_rawset(luaContext->lua, -3); +#if LUA_VERSION_NUM < 502 + luaL_register(luaContext->lua, NULL, _mSTStruct); +#else + luaL_setfuncs(luaContext->lua, _mSTStruct, 0); +#endif lua_pop(luaContext->lua, 1); return &luaContext->d; @@ -129,7 +137,7 @@ bool _luaSetGlobal(struct mScriptEngineContext* ctx, const char* name, struct mS return true; } -struct mScriptValue* _luaWrapFunction(struct mScriptEngineContextLua* luaContext) { +struct mScriptValue* _luaCoerceFunction(struct mScriptEngineContextLua* luaContext) { struct mScriptValue* value = mScriptValueAlloc(&mSTLuaFunc); struct mScriptFunction* fn = calloc(1, sizeof(*fn)); struct mScriptEngineContextLuaRef* ref = calloc(1, sizeof(*ref)); @@ -167,7 +175,18 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) { case LUA_TSTRING: break; case LUA_TFUNCTION: - return _luaWrapFunction(luaContext); + return _luaCoerceFunction(luaContext); + case LUA_TUSERDATA: + if (!lua_getmetatable(luaContext->lua, -1)) { + break; + } + luaL_getmetatable(luaContext->lua, "mSTStruct"); + if (!lua_rawequal(luaContext->lua, -1, -2)) { + lua_pop(luaContext->lua, 2); + break; + } + lua_pop(luaContext->lua, 2); + value = lua_touserdata(luaContext->lua, -1); } lua_pop(luaContext->lua, 1); return value; @@ -178,6 +197,7 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v value = mScriptValueUnwrap(value); } bool ok = true; + struct mScriptValue* newValue; switch (value->type->base) { case mSCRIPT_TYPE_SINT: if (value->type->size <= 4) { @@ -207,14 +227,16 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v } break; case mSCRIPT_TYPE_FUNCTION: - lua_pushlightuserdata(luaContext->lua, value); + newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); + newValue->type = value->type; + newValue->refs = mSCRIPT_VALUE_UNREF; + newValue->type->alloc(newValue); lua_pushcclosure(luaContext->lua, _luaThunk, 1); - mScriptValueRef(value); break; case mSCRIPT_TYPE_OBJECT: - lua_pushlightuserdata(luaContext->lua, value); + newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); + mScriptValueWrap(value, newValue); luaL_setmetatable(luaContext->lua, "mSTStruct"); - mScriptValueRef(value); break; default: ok = false; @@ -308,6 +330,15 @@ bool _luaPopFrame(struct mScriptEngineContextLua* luaContext, struct mScriptList if (count > i) { lua_pop(luaContext->lua, count - i); } + + if (ok) { + for (i = 0; i < (ssize_t) (mScriptListSize(frame) / 2); ++i) { + struct mScriptValue buffer; + memcpy(&buffer, mScriptListGetPointer(frame, i), sizeof(buffer)); + memcpy(mScriptListGetPointer(frame, i), mScriptListGetPointer(frame, mScriptListSize(frame) - i - 1), sizeof(buffer)); + memcpy(mScriptListGetPointer(frame, mScriptListSize(frame) - i - 1), &buffer, sizeof(buffer)); + } + } } return ok; } @@ -346,7 +377,7 @@ bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* return false; } - if (!_luaPopFrame(luaContext, &frame->returnValues)) { + if (frame && !_luaPopFrame(luaContext, &frame->returnValues)) { return false; } diff --git a/src/script/test/lua.c b/src/script/test/lua.c index a112f604c..e6afdda0f 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -324,24 +324,26 @@ M_TEST_DEFINE(callCFunc) { mScriptContextDeinit(&context); } -M_TEST_DEFINE(setGlobalStruct) { +M_TEST_DEFINE(globalStructFieldGet) { struct mScriptContext context; mScriptContextInit(&context); struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); struct Test s = { - .i = 1 + .i = 1, }; - struct mScriptValue a = mSCRIPT_MAKE_S(Test, &s); + struct mScriptValue a; struct mScriptValue b; struct mScriptValue* val; LOAD_PROGRAM("b = a.i"); + + a = mSCRIPT_MAKE_S(Test, &s); assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; assert_true(lua->run(lua)); - b = mSCRIPT_MAKE_S32(1); val = lua->getGlobal(lua, "b"); assert_non_null(val); @@ -350,30 +352,188 @@ M_TEST_DEFINE(setGlobalStruct) { s.i = 2; assert_true(lua->run(lua)); - b = mSCRIPT_MAKE_S32(2); val = lua->getGlobal(lua, "b"); assert_non_null(val); assert_true(b.type->equal(&b, val)); mScriptValueDeref(val); - LOAD_PROGRAM("a.i = b"); - b = mSCRIPT_MAKE_S32(3); - assert_true(lua->setGlobal(lua, "b", &b)); + a = mSCRIPT_MAKE_CS(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; assert_true(lua->run(lua)); + b = mSCRIPT_MAKE_S32(1); + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(b.type->equal(&b, val)); + mScriptValueDeref(val); + lua->destroy(lua); + mScriptContextDeinit(&context); +} + + +M_TEST_DEFINE(globalStructFieldSet) { + struct mScriptContext context; + mScriptContextInit(&context); + struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + + struct Test s = { + .i = 1, + }; + + struct mScriptValue a; + struct mScriptValue b; + + LOAD_PROGRAM("a.i = b"); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + b = mSCRIPT_MAKE_S32(2); + assert_true(lua->setGlobal(lua, "b", &b)); + assert_true(lua->run(lua)); + assert_int_equal(s.i, 2); + + a = mSCRIPT_MAKE_CS(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + b = mSCRIPT_MAKE_S32(2); + assert_true(lua->setGlobal(lua, "b", &b)); + assert_false(lua->run(lua)); + assert_int_equal(s.i, 1); + + lua->destroy(lua); + mScriptContextDeinit(&context); +} + + +M_TEST_DEFINE(globalStructMethods) { + struct mScriptContext context; + mScriptContextInit(&context); + struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context); + + struct Test s = { + .i = 1, + .ifn0 = testI0, + .ifn1 = testI1, + .icfn0 = testIC0, + .vfn0 = testV0, + .vfn1 = testV1, + }; + + struct mScriptValue a; + struct mScriptValue b; + struct mScriptValue* val; + + // ifn0 + LOAD_PROGRAM("b = a:ifn0()"); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + assert_true(lua->run(lua)); + b = mSCRIPT_MAKE_S32(1); + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(b.type->equal(&b, val)); + mScriptValueDeref(val); + + a = mSCRIPT_MAKE_CS(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_false(lua->run(lua)); + + // ifn1 + LOAD_PROGRAM("b = a:ifn1(c)"); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + b = mSCRIPT_MAKE_S32(1); + assert_true(lua->setGlobal(lua, "c", &b)); + assert_true(lua->run(lua)); + b = mSCRIPT_MAKE_S32(2); + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(b.type->equal(&b, val)); + mScriptValueDeref(val); + + b = mSCRIPT_MAKE_S32(2); + assert_true(lua->setGlobal(lua, "c", &b)); + assert_true(lua->run(lua)); + b = mSCRIPT_MAKE_S32(3); + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(b.type->equal(&b, val)); + mScriptValueDeref(val); + + a = mSCRIPT_MAKE_CS(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_false(lua->run(lua)); + + // vfn0 + LOAD_PROGRAM("a:vfn0()"); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + assert_true(lua->run(lua)); + assert_int_equal(s.i, 2); + assert_true(lua->run(lua)); assert_int_equal(s.i, 3); a = mSCRIPT_MAKE_CS(Test, &s); assert_true(lua->setGlobal(lua, "a", &a)); - b = mSCRIPT_MAKE_S32(4); - assert_true(lua->setGlobal(lua, "b", &b)); - assert_false(lua->run(lua)); + // vfn1 + LOAD_PROGRAM("a:vfn1(c)"); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + b = mSCRIPT_MAKE_S32(1); + assert_true(lua->setGlobal(lua, "c", &b)); + assert_true(lua->run(lua)); + assert_int_equal(s.i, 2); + b = mSCRIPT_MAKE_S32(2); + assert_true(lua->setGlobal(lua, "c", &b)); + s.i = 1; + assert_true(lua->run(lua)); assert_int_equal(s.i, 3); + a = mSCRIPT_MAKE_CS(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + b = mSCRIPT_MAKE_S32(1); + assert_true(lua->setGlobal(lua, "c", &b)); + assert_false(lua->run(lua)); + assert_int_equal(s.i, 1); + + // icfn0 + LOAD_PROGRAM("b = a:icfn0()"); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + assert_true(lua->run(lua)); + b = mSCRIPT_MAKE_S32(1); + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(b.type->equal(&b, val)); + mScriptValueDeref(val); + + a = mSCRIPT_MAKE_CS(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + s.i = 1; + assert_true(lua->run(lua)); + b = mSCRIPT_MAKE_S32(1); + val = lua->getGlobal(lua, "b"); + assert_non_null(val); + assert_true(b.type->equal(&b, val)); + mScriptValueDeref(val); + lua->destroy(lua); mScriptContextDeinit(&context); } @@ -387,4 +547,6 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(setGlobal), cmocka_unit_test(callLuaFunc), cmocka_unit_test(callCFunc), - cmocka_unit_test(setGlobalStruct)) + cmocka_unit_test(globalStructFieldGet), + cmocka_unit_test(globalStructFieldSet), + cmocka_unit_test(globalStructMethods))