mirror of https://github.com/mgba-emu/mgba.git
Scripting: Add calling functions with lists
This commit is contained in:
parent
e87ba99140
commit
d6accc4ef6
|
@ -108,6 +108,7 @@ CXX_GUARD_START
|
|||
#define mSCRIPT_TYPE_CMP_F64(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_F64, 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_LIST(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_LIST, 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
|
||||
|
|
|
@ -30,7 +30,7 @@ static bool _luaPushFrame(struct mScriptEngineContextLua*, struct mScriptList*,
|
|||
static bool _luaPopFrame(struct mScriptEngineContextLua*, struct mScriptList*);
|
||||
static bool _luaInvoke(struct mScriptEngineContextLua*, struct mScriptFrame*);
|
||||
|
||||
static struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext);
|
||||
static struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, bool pop);
|
||||
static bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue*);
|
||||
|
||||
static void _luaDeref(struct mScriptValue*);
|
||||
|
@ -186,7 +186,7 @@ bool _luaIsScript(struct mScriptEngineContext* ctx, const char* name, struct VFi
|
|||
struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext* ctx, const char* name) {
|
||||
struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx;
|
||||
lua_getglobal(luaContext->lua, name);
|
||||
return _luaCoerce(luaContext);
|
||||
return _luaCoerce(luaContext, true);
|
||||
}
|
||||
|
||||
bool _luaSetGlobal(struct mScriptEngineContext* ctx, const char* name, struct mScriptValue* value) {
|
||||
|
@ -212,7 +212,83 @@ struct mScriptValue* _luaCoerceFunction(struct mScriptEngineContextLua* luaConte
|
|||
return value;
|
||||
}
|
||||
|
||||
struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) {
|
||||
struct mScriptValue* _luaCoerceTable(struct mScriptEngineContextLua* luaContext) {
|
||||
struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE);
|
||||
bool isList = true;
|
||||
|
||||
lua_pushnil(luaContext->lua);
|
||||
while (lua_next(luaContext->lua, -2) != 0) {
|
||||
struct mScriptValue* value = NULL;
|
||||
int type = lua_type(luaContext->lua, -1);
|
||||
switch (type) {
|
||||
case LUA_TNUMBER:
|
||||
case LUA_TBOOLEAN:
|
||||
case LUA_TSTRING:
|
||||
case LUA_TFUNCTION:
|
||||
value = _luaCoerce(luaContext, true);
|
||||
break;
|
||||
default:
|
||||
// Don't let values be something that could contain themselves
|
||||
break;
|
||||
}
|
||||
if (!value) {
|
||||
lua_pop(luaContext->lua, 3);
|
||||
mScriptValueDeref(table);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct mScriptValue* key = NULL;
|
||||
type = lua_type(luaContext->lua, -1);
|
||||
switch (type) {
|
||||
case LUA_TBOOLEAN:
|
||||
case LUA_TSTRING:
|
||||
isList = false;
|
||||
// Fall through
|
||||
case LUA_TNUMBER:
|
||||
key = _luaCoerce(luaContext, false);
|
||||
break;
|
||||
default:
|
||||
// Limit keys to hashable types
|
||||
break;
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
lua_pop(luaContext->lua, 2);
|
||||
mScriptValueDeref(table);
|
||||
return false;
|
||||
}
|
||||
mScriptTableInsert(table, key, value);
|
||||
mScriptValueDeref(key);
|
||||
mScriptValueDeref(value);
|
||||
}
|
||||
lua_pop(luaContext->lua, 1);
|
||||
|
||||
size_t len = mScriptTableSize(table);
|
||||
if (!isList || !len) {
|
||||
return table;
|
||||
}
|
||||
|
||||
struct mScriptValue* list = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST);
|
||||
size_t i;
|
||||
for (i = 1; i <= len; ++i) {
|
||||
struct mScriptValue* value = mScriptTableLookup(table, &mSCRIPT_MAKE_S64(i));
|
||||
if (!value) {
|
||||
mScriptValueDeref(list);
|
||||
return table;
|
||||
}
|
||||
mScriptValueWrap(value, mScriptListAppend(list->value.list));
|
||||
}
|
||||
if (i != len + 1) {
|
||||
mScriptValueDeref(list);
|
||||
mScriptContextFillPool(luaContext->d.context, table);
|
||||
return table;
|
||||
}
|
||||
mScriptValueDeref(table);
|
||||
mScriptContextFillPool(luaContext->d.context, list);
|
||||
return list;
|
||||
}
|
||||
|
||||
struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext, bool pop) {
|
||||
if (lua_isnone(luaContext->lua, -1)) {
|
||||
lua_pop(luaContext->lua, 1);
|
||||
return NULL;
|
||||
|
@ -244,7 +320,16 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) {
|
|||
break;
|
||||
case LUA_TFUNCTION:
|
||||
// This function pops the value internally via luaL_ref
|
||||
if (!pop) {
|
||||
break;
|
||||
}
|
||||
return _luaCoerceFunction(luaContext);
|
||||
case LUA_TTABLE:
|
||||
// This function pops the value internally via luaL_ref
|
||||
if (!pop) {
|
||||
break;
|
||||
}
|
||||
return _luaCoerceTable(luaContext);
|
||||
case LUA_TUSERDATA:
|
||||
if (!lua_getmetatable(luaContext->lua, -1)) {
|
||||
break;
|
||||
|
@ -259,7 +344,9 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) {
|
|||
value = mScriptContextAccessWeakref(luaContext->d.context, value);
|
||||
break;
|
||||
}
|
||||
lua_pop(luaContext->lua, 1);
|
||||
if (pop) {
|
||||
lua_pop(luaContext->lua, 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -467,7 +554,7 @@ bool _luaPopFrame(struct mScriptEngineContextLua* luaContext, struct mScriptList
|
|||
if (frame) {
|
||||
int i;
|
||||
for (i = 0; i < count; ++i) {
|
||||
struct mScriptValue* value = _luaCoerce(luaContext);
|
||||
struct mScriptValue* value = _luaCoerce(luaContext, true);
|
||||
if (!value) {
|
||||
ok = false;
|
||||
break;
|
||||
|
@ -640,7 +727,7 @@ int _luaSetObject(lua_State* lua) {
|
|||
char key[MAX_KEY_SIZE];
|
||||
const char* keyPtr = lua_tostring(lua, -2);
|
||||
struct mScriptValue* obj = lua_touserdata(lua, -3);
|
||||
struct mScriptValue* val = _luaCoerce(luaContext);
|
||||
struct mScriptValue* val = _luaCoerce(luaContext, true);
|
||||
|
||||
if (!keyPtr) {
|
||||
lua_pop(lua, 2);
|
||||
|
|
|
@ -61,8 +61,22 @@ static void testV1(struct Test* a, int b) {
|
|||
a->i += b;
|
||||
}
|
||||
|
||||
static int32_t sum(struct mScriptList* list) {
|
||||
int32_t sum = 0;
|
||||
size_t i;
|
||||
for (i = 0; i < mScriptListSize(list); ++i) {
|
||||
struct mScriptValue value;
|
||||
if (!mScriptCast(mSCRIPT_TYPE_MS_S32, mScriptListGetPointer(list, i), &value)) {
|
||||
continue;
|
||||
}
|
||||
sum += value.value.s32;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
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_DECLARE_STRUCT(Test);
|
||||
mSCRIPT_DECLARE_STRUCT_D_METHOD(Test, S32, ifn0, 0);
|
||||
|
@ -619,6 +633,24 @@ M_TEST_DEFINE(tableIterate) {
|
|||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(callList) {
|
||||
SETUP_LUA;
|
||||
|
||||
struct mScriptValue a = mSCRIPT_MAKE_S32(6);
|
||||
struct mScriptValue* val;
|
||||
|
||||
assert_true(lua->setGlobal(lua, "sum", &boundSum));
|
||||
TEST_PROGRAM("a = sum({1, 2, 3})");
|
||||
assert_null(lua->getError(lua));
|
||||
|
||||
val = lua->getGlobal(lua, "a");
|
||||
assert_non_null(val);
|
||||
assert_true(mSCRIPT_TYPE_MS_S32->equal(&a, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua,
|
||||
cmocka_unit_test(create),
|
||||
cmocka_unit_test(loadGood),
|
||||
|
@ -634,4 +666,5 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua,
|
|||
cmocka_unit_test(errorReporting),
|
||||
cmocka_unit_test(tableLookup),
|
||||
cmocka_unit_test(tableIterate),
|
||||
cmocka_unit_test(callList),
|
||||
)
|
||||
|
|
|
@ -52,6 +52,30 @@ static int isHello(const char* str) {
|
|||
return strcmp(str, "hello") == 0;
|
||||
}
|
||||
|
||||
static int isSequential(struct mScriptList* list) {
|
||||
int last;
|
||||
if (mScriptListSize(list) == 0) {
|
||||
return true;
|
||||
}
|
||||
size_t i;
|
||||
for (i = 0; i < mScriptListSize(list); ++i) {
|
||||
struct mScriptValue* value = mScriptListGetPointer(list, i);
|
||||
struct mScriptValue intValue;
|
||||
if (!mScriptCast(mSCRIPT_TYPE_MS_S32, value, &intValue)) {
|
||||
return false;
|
||||
}
|
||||
if (!i) {
|
||||
last = intValue.value.s32;
|
||||
} else {
|
||||
if (intValue.value.s32 != last + 1) {
|
||||
return false;
|
||||
}
|
||||
++last;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0);
|
||||
mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32, ignored);
|
||||
mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32, in);
|
||||
|
@ -61,6 +85,7 @@ mSCRIPT_BIND_FUNCTION(boundIdentityStruct, S(Test), identityStruct, 1, S(Test),
|
|||
mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, a, S32, b);
|
||||
mSCRIPT_BIND_FUNCTION(boundSubInts, S32, subInts, 2, S32, a, S32, b);
|
||||
mSCRIPT_BIND_FUNCTION(boundIsHello, S32, isHello, 1, CHARP, str);
|
||||
mSCRIPT_BIND_FUNCTION(boundIsSequential, S32, isSequential, 1, LIST, list);
|
||||
|
||||
M_TEST_DEFINE(voidArgs) {
|
||||
struct mScriptFrame frame;
|
||||
|
@ -919,6 +944,47 @@ M_TEST_DEFINE(stringIsNotHello) {
|
|||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(invokeList) {
|
||||
struct mScriptFrame frame;
|
||||
struct mScriptList list;
|
||||
int val;
|
||||
|
||||
mScriptListInit(&list, 0);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, LIST, &list);
|
||||
assert_true(mScriptInvoke(&boundIsSequential, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &val));
|
||||
assert_int_equal(val, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
*mScriptListAppend(&list) = mSCRIPT_MAKE_S32(1);
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, LIST, &list);
|
||||
assert_true(mScriptInvoke(&boundIsSequential, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &val));
|
||||
assert_int_equal(val, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
*mScriptListAppend(&list) = mSCRIPT_MAKE_S32(2);
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, LIST, &list);
|
||||
assert_true(mScriptInvoke(&boundIsSequential, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &val));
|
||||
assert_int_equal(val, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
*mScriptListAppend(&list) = mSCRIPT_MAKE_S32(4);
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, LIST, &list);
|
||||
assert_true(mScriptInvoke(&boundIsSequential, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &val));
|
||||
assert_int_equal(val, 0);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptListDeinit(&list);
|
||||
}
|
||||
|
||||
M_TEST_SUITE_DEFINE(mScript,
|
||||
cmocka_unit_test(voidArgs),
|
||||
cmocka_unit_test(voidFunc),
|
||||
|
@ -948,4 +1014,6 @@ M_TEST_SUITE_DEFINE(mScript,
|
|||
cmocka_unit_test(hashTableBasic),
|
||||
cmocka_unit_test(hashTableString),
|
||||
cmocka_unit_test(stringIsHello),
|
||||
cmocka_unit_test(stringIsNotHello))
|
||||
cmocka_unit_test(stringIsNotHello),
|
||||
cmocka_unit_test(invokeList),
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue