mirror of https://github.com/mgba-emu/mgba.git
Scripting: Add weak references for opaque runtime access
This commit is contained in:
parent
7bb051b01d
commit
af44a65c3d
|
@ -21,6 +21,8 @@ struct mScriptEngineContext;
|
|||
struct mScriptContext {
|
||||
struct Table rootScope;
|
||||
struct Table engines;
|
||||
struct Table weakrefs;
|
||||
uint32_t nextWeakref;
|
||||
};
|
||||
|
||||
struct mScriptEngine2 {
|
||||
|
@ -55,6 +57,11 @@ void mScriptContextRegisterEngines(struct mScriptContext*);
|
|||
void mScriptContextSetGlobal(struct mScriptContext*, const char* key, struct mScriptValue* value);
|
||||
void mScriptContextRemoveGlobal(struct mScriptContext*, const char* key);
|
||||
|
||||
uint32_t mScriptContextSetWeakref(struct mScriptContext*, struct mScriptValue* value);
|
||||
struct mScriptValue* mScriptContextMakeWeakref(struct mScriptContext*, struct mScriptValue* value);
|
||||
struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct mScriptValue* value);
|
||||
void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref);
|
||||
|
||||
struct VFile;
|
||||
bool mScriptContextLoadVF(struct mScriptContext*, const char* name, struct VFile* vf);
|
||||
bool mScriptContextLoadFile(struct mScriptContext*, const char* path);
|
||||
|
|
|
@ -32,6 +32,7 @@ CXX_GUARD_START
|
|||
#define mSCRIPT_TYPE_C_PTR void*
|
||||
#define mSCRIPT_TYPE_C_TABLE Table*
|
||||
#define mSCRIPT_TYPE_C_WRAPPER struct mScriptValue*
|
||||
#define mSCRIPT_TYPE_C_WEAKREF uint32_t
|
||||
#define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT*
|
||||
#define mSCRIPT_TYPE_C_CS(STRUCT) const struct STRUCT*
|
||||
#define mSCRIPT_TYPE_C_S_METHOD(STRUCT, NAME) _mSTStructFunctionType_ ## STRUCT ## _ ## NAME
|
||||
|
@ -51,6 +52,7 @@ CXX_GUARD_START
|
|||
#define mSCRIPT_TYPE_FIELD_PTR opaque
|
||||
#define mSCRIPT_TYPE_FIELD_TABLE opaque
|
||||
#define mSCRIPT_TYPE_FIELD_WRAPPER opaque
|
||||
#define mSCRIPT_TYPE_FIELD_WEAKREF u32
|
||||
#define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque
|
||||
#define mSCRIPT_TYPE_FIELD_CS(STRUCT) copaque
|
||||
#define mSCRIPT_TYPE_FIELD_S_METHOD(STRUCT, NAME) copaque
|
||||
|
@ -69,6 +71,7 @@ CXX_GUARD_START
|
|||
#define mSCRIPT_TYPE_MS_CHARP (&mSTCharPtr)
|
||||
#define mSCRIPT_TYPE_MS_TABLE (&mSTTable)
|
||||
#define mSCRIPT_TYPE_MS_WRAPPER (&mSTWrapper)
|
||||
#define mSCRIPT_TYPE_MS_WEAKREF (&mSTWeakref)
|
||||
#define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT)
|
||||
#define mSCRIPT_TYPE_MS_CS(STRUCT) (&mSTStructConst_ ## STRUCT)
|
||||
#define mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME) (&_mSTStructBindingType_ ## STRUCT ## _ ## NAME)
|
||||
|
@ -435,7 +438,8 @@ enum mScriptTypeBase {
|
|||
mSCRIPT_TYPE_TUPLE,
|
||||
mSCRIPT_TYPE_LIST,
|
||||
mSCRIPT_TYPE_TABLE,
|
||||
mSCRIPT_TYPE_WRAPPER
|
||||
mSCRIPT_TYPE_WRAPPER,
|
||||
mSCRIPT_TYPE_WEAKREF,
|
||||
};
|
||||
|
||||
enum mScriptClassInitType {
|
||||
|
@ -462,6 +466,7 @@ extern const struct mScriptType mSTString;
|
|||
extern const struct mScriptType mSTCharPtr;
|
||||
extern const struct mScriptType mSTTable;
|
||||
extern const struct mScriptType mSTWrapper;
|
||||
extern const struct mScriptType mSTWeakref;
|
||||
|
||||
struct mScriptTypeTuple {
|
||||
size_t count;
|
||||
|
|
|
@ -120,6 +120,33 @@ M_TEST_DEFINE(infoFuncs) {
|
|||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(detach) {
|
||||
SETUP_LUA;
|
||||
CREATE_CORE;
|
||||
core->reset(core);
|
||||
|
||||
LOAD_PROGRAM(
|
||||
"assert(emu)\n"
|
||||
"a = emu\n"
|
||||
);
|
||||
assert_true(lua->run(lua));
|
||||
|
||||
mScriptContextDetachCore(&context);
|
||||
|
||||
LOAD_PROGRAM(
|
||||
"assert(not emu)\n"
|
||||
);
|
||||
assert_true(lua->run(lua));
|
||||
|
||||
LOAD_PROGRAM(
|
||||
"a:frequency()\n"
|
||||
);
|
||||
assert_false(lua->run(lua));
|
||||
|
||||
TEARDOWN_CORE;
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(runFrame) {
|
||||
SETUP_LUA;
|
||||
CREATE_CORE;
|
||||
|
@ -206,6 +233,7 @@ M_TEST_DEFINE(memoryWrite) {
|
|||
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore,
|
||||
cmocka_unit_test(globals),
|
||||
cmocka_unit_test(infoFuncs),
|
||||
cmocka_unit_test(detach),
|
||||
cmocka_unit_test(runFrame),
|
||||
cmocka_unit_test(memoryRead),
|
||||
cmocka_unit_test(memoryWrite),
|
||||
|
|
|
@ -52,11 +52,14 @@ static void _contextFindForFile(const char* key, void* value, void* user) {
|
|||
void mScriptContextInit(struct mScriptContext* context) {
|
||||
HashTableInit(&context->rootScope, 0, (void (*)(void*)) mScriptValueDeref);
|
||||
HashTableInit(&context->engines, 0, _engineContextDestroy);
|
||||
TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref);
|
||||
context->nextWeakref = 0;
|
||||
}
|
||||
|
||||
void mScriptContextDeinit(struct mScriptContext* context) {
|
||||
HashTableDeinit(&context->engines);
|
||||
HashTableDeinit(&context->rootScope);
|
||||
HashTableDeinit(&context->weakrefs);
|
||||
}
|
||||
|
||||
struct mScriptEngineContext* mScriptContextRegisterEngine(struct mScriptContext* context, struct mScriptEngine2* engine) {
|
||||
|
@ -75,7 +78,13 @@ void mScriptContextRegisterEngines(struct mScriptContext* context) {
|
|||
}
|
||||
|
||||
void mScriptContextSetGlobal(struct mScriptContext* context, const char* key, struct mScriptValue* value) {
|
||||
mScriptValueRef(value);
|
||||
struct mScriptValue* oldValue = HashTableLookup(&context->rootScope, key);
|
||||
if (oldValue) {
|
||||
mScriptContextClearWeakref(context, oldValue->value.u32);
|
||||
}
|
||||
uint32_t weakref = mScriptContextSetWeakref(context, value);
|
||||
value = mScriptValueAlloc(mSCRIPT_TYPE_MS_WEAKREF);
|
||||
value->value.u32 = weakref;
|
||||
HashTableInsert(&context->rootScope, key, value);
|
||||
struct mScriptKVPair pair = {
|
||||
.key = key,
|
||||
|
@ -90,7 +99,42 @@ void mScriptContextRemoveGlobal(struct mScriptContext* context, const char* key)
|
|||
}
|
||||
// Since _contextRemoveGlobal doesn't mutate |key|, this cast should be safe
|
||||
HashTableEnumerate(&context->engines, _contextRemoveGlobal, (char*) key);
|
||||
HashTableRemove(&context->rootScope, key);
|
||||
struct mScriptValue* oldValue = HashTableLookup(&context->rootScope, key);
|
||||
if (oldValue) {
|
||||
mScriptContextClearWeakref(context, oldValue->value.u32);
|
||||
HashTableRemove(&context->rootScope, key);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t mScriptContextSetWeakref(struct mScriptContext* context, struct mScriptValue* value) {
|
||||
mScriptValueRef(value);
|
||||
TableInsert(&context->weakrefs, context->nextWeakref, value);
|
||||
|
||||
uint32_t nextWeakref = context->nextWeakref;
|
||||
++context->nextWeakref;
|
||||
while (TableLookup(&context->weakrefs, context->nextWeakref)) {
|
||||
++context->nextWeakref;
|
||||
}
|
||||
return nextWeakref;
|
||||
}
|
||||
|
||||
struct mScriptValue* mScriptContextMakeWeakref(struct mScriptContext* context, struct mScriptValue* value) {
|
||||
uint32_t weakref = mScriptContextSetWeakref(context, value);
|
||||
mScriptValueDeref(value);
|
||||
value = mScriptValueAlloc(mSCRIPT_TYPE_MS_WEAKREF);
|
||||
value->value.u32 = weakref;
|
||||
return value;
|
||||
}
|
||||
|
||||
struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext* context, struct mScriptValue* value) {
|
||||
if (value->type != mSCRIPT_TYPE_MS_WEAKREF) {
|
||||
return value;
|
||||
}
|
||||
return TableLookup(&context->weakrefs, value->value.u32);
|
||||
}
|
||||
|
||||
void mScriptContextClearWeakref(struct mScriptContext* context, uint32_t weakref) {
|
||||
TableRemove(&context->weakrefs, weakref);
|
||||
}
|
||||
|
||||
bool mScriptContextLoadVF(struct mScriptContext* context, const char* name, struct VFile* vf) {
|
||||
|
|
|
@ -212,15 +212,23 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) {
|
|||
}
|
||||
lua_pop(luaContext->lua, 2);
|
||||
value = lua_touserdata(luaContext->lua, -1);
|
||||
value = mScriptContextAccessWeakref(luaContext->d.context, value);
|
||||
}
|
||||
lua_pop(luaContext->lua, 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* value) {
|
||||
uint32_t weakref;
|
||||
bool needsWeakref = false;
|
||||
if (value->type == mSCRIPT_TYPE_MS_WRAPPER) {
|
||||
value = mScriptValueUnwrap(value);
|
||||
}
|
||||
if (value->type == mSCRIPT_TYPE_MS_WEAKREF) {
|
||||
weakref = value->value.u32;
|
||||
value = mScriptContextAccessWeakref(luaContext->d.context, value);
|
||||
needsWeakref = true;
|
||||
}
|
||||
bool ok = true;
|
||||
struct mScriptValue* newValue;
|
||||
switch (value->type->base) {
|
||||
|
@ -260,7 +268,11 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v
|
|||
break;
|
||||
case mSCRIPT_TYPE_OBJECT:
|
||||
newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue));
|
||||
mScriptValueWrap(value, newValue);
|
||||
if (needsWeakref) {
|
||||
*newValue = mSCRIPT_MAKE(WEAKREF, weakref);
|
||||
} else {
|
||||
mScriptValueWrap(value, newValue);
|
||||
}
|
||||
luaL_setmetatable(luaContext->lua, "mSTStruct");
|
||||
break;
|
||||
default:
|
||||
|
@ -484,6 +496,13 @@ int _luaGetObject(lua_State* lua) {
|
|||
struct mScriptValue* obj = lua_touserdata(lua, -2);
|
||||
struct mScriptValue val;
|
||||
|
||||
obj = mScriptContextAccessWeakref(luaContext->d.context, obj);
|
||||
if (!obj) {
|
||||
lua_pop(lua, 2);
|
||||
lua_pushliteral(lua, "Invalid object");
|
||||
lua_error(lua);
|
||||
}
|
||||
|
||||
if (!mScriptObjectGet(obj, key, &val)) {
|
||||
lua_pop(lua, 2);
|
||||
lua_pushliteral(lua, "Invalid key");
|
||||
|
@ -505,6 +524,12 @@ int _luaSetObject(lua_State* lua) {
|
|||
struct mScriptValue* obj = lua_touserdata(lua, -3);
|
||||
struct mScriptValue* val = _luaCoerce(luaContext);
|
||||
|
||||
obj = mScriptContextAccessWeakref(luaContext->d.context, obj);
|
||||
if (!obj) {
|
||||
lua_pushliteral(lua, "Invalid object");
|
||||
lua_error(lua);
|
||||
}
|
||||
|
||||
lua_pop(lua, 2);
|
||||
if (!val) {
|
||||
lua_pushliteral(lua, "Invalid value");
|
||||
|
|
|
@ -195,6 +195,15 @@ const struct mScriptType mSTWrapper = {
|
|||
.hash = NULL,
|
||||
};
|
||||
|
||||
const struct mScriptType mSTWeakref = {
|
||||
.base = mSCRIPT_TYPE_WEAKREF,
|
||||
.size = sizeof(uint32_t),
|
||||
.name = "weakref",
|
||||
.alloc = NULL,
|
||||
.free = NULL,
|
||||
.hash = NULL,
|
||||
};
|
||||
|
||||
DEFINE_VECTOR(mScriptList, struct mScriptValue)
|
||||
|
||||
void _allocTable(struct mScriptValue* val) {
|
||||
|
|
Loading…
Reference in New Issue