Scripting: Allow Lua to pass nested tables to the scripting subsystem

This commit is contained in:
Vicki Pfau 2023-01-31 17:22:29 -08:00
parent f37d068733
commit 5164b888d8
3 changed files with 71 additions and 7 deletions

View File

@ -35,7 +35,7 @@ CXX_GUARD_START
#define mSCRIPT_TYPE_C_PTR void*
#define mSCRIPT_TYPE_C_CPTR const void*
#define mSCRIPT_TYPE_C_LIST struct mScriptList*
#define mSCRIPT_TYPE_C_TABLE Table*
#define mSCRIPT_TYPE_C_TABLE struct Table*
#define mSCRIPT_TYPE_C_WRAPPER struct mScriptValue*
#define mSCRIPT_TYPE_C_WEAKREF uint32_t
#define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT*
@ -120,6 +120,7 @@ CXX_GUARD_START
#define mSCRIPT_TYPE_CMP_STR(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_STR, TYPE)
#define mSCRIPT_TYPE_CMP_CHARP(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE)
#define mSCRIPT_TYPE_CMP_LIST(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_LIST, TYPE)
#define mSCRIPT_TYPE_CMP_TABLE(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_TABLE, TYPE)
#define mSCRIPT_TYPE_CMP_PTR(TYPE) ((TYPE)->base >= mSCRIPT_TYPE_OPAQUE)
#define mSCRIPT_TYPE_CMP_WRAPPER(TYPE) (true)
#define mSCRIPT_TYPE_CMP_S(STRUCT) mSCRIPT_TYPE_MS_S(STRUCT)->name == _mSCRIPT_FIELD_NAME

View File

@ -538,11 +538,13 @@ struct mScriptValue* _luaCoerceFunction(struct mScriptEngineContextLua* luaConte
return value;
}
struct mScriptValue* _luaCoerceTable(struct mScriptEngineContextLua* luaContext) {
struct mScriptValue* _luaCoerceTable(struct mScriptEngineContextLua* luaContext, struct Table* markedObjects) {
struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE);
bool isList = true;
lua_pushnil(luaContext->lua);
void* tablePointer;
while (lua_next(luaContext->lua, -2) != 0) {
struct mScriptValue* value = NULL;
int type = lua_type(luaContext->lua, -1);
@ -553,14 +555,23 @@ struct mScriptValue* _luaCoerceTable(struct mScriptEngineContextLua* luaContext)
case LUA_TFUNCTION:
value = _luaCoerce(luaContext, true);
break;
case LUA_TTABLE:
tablePointer = lua_topointer(luaContext->lua, -1);
// Ensure this table doesn't contain any cycles
if (!HashTableLookupBinary(markedObjects, &tablePointer, sizeof(tablePointer))) {
HashTableInsertBinary(markedObjects, &tablePointer, sizeof(tablePointer), tablePointer);
value = _luaCoerceTable(luaContext, markedObjects);
if (value) {
mScriptValueRef(value);
}
}
default:
// Don't let values be something that could contain themselves
break;
}
if (!value) {
lua_pop(luaContext->lua, 3);
lua_pop(luaContext->lua, type == LUA_TTABLE ? 2 : 3);
mScriptValueDeref(table);
return false;
return NULL;
}
struct mScriptValue* key = NULL;
@ -628,6 +639,7 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, bool
size_t size;
const void* buffer;
struct Table markedObjects;
struct mScriptValue* value = NULL;
switch (lua_type(luaContext->lua, -1)) {
case LUA_TNIL:
@ -664,7 +676,10 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, bool
if (!pop) {
break;
}
return _luaCoerceTable(luaContext);
HashTableInit(&markedObjects, 0, NULL);
value = _luaCoerceTable(luaContext, &markedObjects);
HashTableDeinit(&markedObjects);
return value;
case LUA_TUSERDATA:
if (!lua_getmetatable(luaContext->lua, -1)) {
break;

View File

@ -66,9 +66,14 @@ static int32_t sum(struct mScriptList* list) {
return sum;
}
static unsigned tableSize(struct Table* table) {
return TableSize(table);
}
mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32, a);
mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, a, S32, b);
mSCRIPT_BIND_FUNCTION(boundSum, S32, sum, 1, LIST, list);
mSCRIPT_BIND_FUNCTION(boundTableSize, U32, tableSize, 1, TABLE, table);
mSCRIPT_DECLARE_STRUCT(Test);
mSCRIPT_DECLARE_STRUCT_D_METHOD(Test, S32, ifn0, 0);
@ -371,8 +376,50 @@ M_TEST_DEFINE(callCFunc) {
assert_true(a.type->equal(&a, val));
mScriptValueDeref(val);
LOAD_PROGRAM("b('a')");
assert_false(lua->run(lua));
mScriptContextDeinit(&context);
}
M_TEST_DEFINE(callCTable) {
SETUP_LUA;
struct mScriptValue a = mSCRIPT_MAKE_S32(1);
struct mScriptValue* val;
assert_true(lua->setGlobal(lua, "b", &boundTableSize));
TEST_PROGRAM("assert(b({}) == 0)");
assert_null(lua->getError(lua));
TEST_PROGRAM("assert(b({[2]=1}) == 1)");
assert_null(lua->getError(lua));
TEST_PROGRAM("assert(b({a=1}) == 1)");
assert_null(lua->getError(lua));
TEST_PROGRAM("assert(b({a={}}) == 1)");
assert_null(lua->getError(lua));
LOAD_PROGRAM(
"a = {}\n"
"a.b = a\n"
"assert(b(a) == 1)\n"
);
assert_false(lua->run(lua));
LOAD_PROGRAM(
"a = {}\n"
"a.b = {}\n"
"a.b.c = a\n"
"assert(b(a) == 1)\n"
);
assert_false(lua->run(lua));
mScriptContextDeinit(&context);
}
M_TEST_DEFINE(globalNull) {
SETUP_LUA;
@ -774,6 +821,7 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua,
cmocka_unit_test(rootScope),
cmocka_unit_test(callLuaFunc),
cmocka_unit_test(callCFunc),
cmocka_unit_test(callCTable),
cmocka_unit_test(globalNull),
cmocka_unit_test(globalStructFieldGet),
cmocka_unit_test(globalStructFieldSet),
@ -782,5 +830,5 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua,
cmocka_unit_test(tableLookup),
cmocka_unit_test(tableIterate),
cmocka_unit_test(callList),
cmocka_unit_test(linkedList)
cmocka_unit_test(linkedList),
)