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_PTR void*
#define mSCRIPT_TYPE_C_CPTR const void* #define mSCRIPT_TYPE_C_CPTR const void*
#define mSCRIPT_TYPE_C_LIST struct mScriptList* #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_WRAPPER struct mScriptValue*
#define mSCRIPT_TYPE_C_WEAKREF uint32_t #define mSCRIPT_TYPE_C_WEAKREF uint32_t
#define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT* #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_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_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_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_PTR(TYPE) ((TYPE)->base >= mSCRIPT_TYPE_OPAQUE)
#define mSCRIPT_TYPE_CMP_WRAPPER(TYPE) (true) #define mSCRIPT_TYPE_CMP_WRAPPER(TYPE) (true)
#define mSCRIPT_TYPE_CMP_S(STRUCT) mSCRIPT_TYPE_MS_S(STRUCT)->name == _mSCRIPT_FIELD_NAME #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; 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); struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE);
bool isList = true; bool isList = true;
lua_pushnil(luaContext->lua); lua_pushnil(luaContext->lua);
void* tablePointer;
while (lua_next(luaContext->lua, -2) != 0) { while (lua_next(luaContext->lua, -2) != 0) {
struct mScriptValue* value = NULL; struct mScriptValue* value = NULL;
int type = lua_type(luaContext->lua, -1); int type = lua_type(luaContext->lua, -1);
@ -553,14 +555,23 @@ struct mScriptValue* _luaCoerceTable(struct mScriptEngineContextLua* luaContext)
case LUA_TFUNCTION: case LUA_TFUNCTION:
value = _luaCoerce(luaContext, true); value = _luaCoerce(luaContext, true);
break; 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: default:
// Don't let values be something that could contain themselves
break; break;
} }
if (!value) { if (!value) {
lua_pop(luaContext->lua, 3); lua_pop(luaContext->lua, type == LUA_TTABLE ? 2 : 3);
mScriptValueDeref(table); mScriptValueDeref(table);
return false; return NULL;
} }
struct mScriptValue* key = NULL; struct mScriptValue* key = NULL;
@ -628,6 +639,7 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, bool
size_t size; size_t size;
const void* buffer; const void* buffer;
struct Table markedObjects;
struct mScriptValue* value = NULL; struct mScriptValue* value = NULL;
switch (lua_type(luaContext->lua, -1)) { switch (lua_type(luaContext->lua, -1)) {
case LUA_TNIL: case LUA_TNIL:
@ -664,7 +676,10 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, bool
if (!pop) { if (!pop) {
break; break;
} }
return _luaCoerceTable(luaContext); HashTableInit(&markedObjects, 0, NULL);
value = _luaCoerceTable(luaContext, &markedObjects);
HashTableDeinit(&markedObjects);
return value;
case LUA_TUSERDATA: case LUA_TUSERDATA:
if (!lua_getmetatable(luaContext->lua, -1)) { if (!lua_getmetatable(luaContext->lua, -1)) {
break; break;

View File

@ -66,9 +66,14 @@ static int32_t sum(struct mScriptList* list) {
return sum; return sum;
} }
static unsigned tableSize(struct Table* table) {
return TableSize(table);
}
mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32, a); mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32, a);
mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, a, S32, b); mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, a, S32, b);
mSCRIPT_BIND_FUNCTION(boundSum, S32, sum, 1, LIST, list); 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(Test);
mSCRIPT_DECLARE_STRUCT_D_METHOD(Test, S32, ifn0, 0); mSCRIPT_DECLARE_STRUCT_D_METHOD(Test, S32, ifn0, 0);
@ -371,8 +376,50 @@ M_TEST_DEFINE(callCFunc) {
assert_true(a.type->equal(&a, val)); assert_true(a.type->equal(&a, val));
mScriptValueDeref(val); mScriptValueDeref(val);
LOAD_PROGRAM("b('a')");
assert_false(lua->run(lua));
mScriptContextDeinit(&context); 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) { M_TEST_DEFINE(globalNull) {
SETUP_LUA; SETUP_LUA;
@ -774,6 +821,7 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua,
cmocka_unit_test(rootScope), cmocka_unit_test(rootScope),
cmocka_unit_test(callLuaFunc), cmocka_unit_test(callLuaFunc),
cmocka_unit_test(callCFunc), cmocka_unit_test(callCFunc),
cmocka_unit_test(callCTable),
cmocka_unit_test(globalNull), cmocka_unit_test(globalNull),
cmocka_unit_test(globalStructFieldGet), cmocka_unit_test(globalStructFieldGet),
cmocka_unit_test(globalStructFieldSet), cmocka_unit_test(globalStructFieldSet),
@ -782,5 +830,5 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua,
cmocka_unit_test(tableLookup), cmocka_unit_test(tableLookup),
cmocka_unit_test(tableIterate), cmocka_unit_test(tableIterate),
cmocka_unit_test(callList), cmocka_unit_test(callList),
cmocka_unit_test(linkedList) cmocka_unit_test(linkedList),
) )