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 mScriptContext {
|
||||||
struct Table rootScope;
|
struct Table rootScope;
|
||||||
struct Table engines;
|
struct Table engines;
|
||||||
|
struct Table weakrefs;
|
||||||
|
uint32_t nextWeakref;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mScriptEngine2 {
|
struct mScriptEngine2 {
|
||||||
|
@ -55,6 +57,11 @@ void mScriptContextRegisterEngines(struct mScriptContext*);
|
||||||
void mScriptContextSetGlobal(struct mScriptContext*, const char* key, struct mScriptValue* value);
|
void mScriptContextSetGlobal(struct mScriptContext*, const char* key, struct mScriptValue* value);
|
||||||
void mScriptContextRemoveGlobal(struct mScriptContext*, const char* key);
|
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;
|
struct VFile;
|
||||||
bool mScriptContextLoadVF(struct mScriptContext*, const char* name, struct VFile* vf);
|
bool mScriptContextLoadVF(struct mScriptContext*, const char* name, struct VFile* vf);
|
||||||
bool mScriptContextLoadFile(struct mScriptContext*, const char* path);
|
bool mScriptContextLoadFile(struct mScriptContext*, const char* path);
|
||||||
|
|
|
@ -32,6 +32,7 @@ CXX_GUARD_START
|
||||||
#define mSCRIPT_TYPE_C_PTR void*
|
#define mSCRIPT_TYPE_C_PTR void*
|
||||||
#define mSCRIPT_TYPE_C_TABLE Table*
|
#define mSCRIPT_TYPE_C_TABLE 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_S(STRUCT) struct STRUCT*
|
#define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT*
|
||||||
#define mSCRIPT_TYPE_C_CS(STRUCT) const struct STRUCT*
|
#define mSCRIPT_TYPE_C_CS(STRUCT) const struct STRUCT*
|
||||||
#define mSCRIPT_TYPE_C_S_METHOD(STRUCT, NAME) _mSTStructFunctionType_ ## STRUCT ## _ ## NAME
|
#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_PTR opaque
|
||||||
#define mSCRIPT_TYPE_FIELD_TABLE opaque
|
#define mSCRIPT_TYPE_FIELD_TABLE opaque
|
||||||
#define mSCRIPT_TYPE_FIELD_WRAPPER opaque
|
#define mSCRIPT_TYPE_FIELD_WRAPPER opaque
|
||||||
|
#define mSCRIPT_TYPE_FIELD_WEAKREF u32
|
||||||
#define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque
|
#define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque
|
||||||
#define mSCRIPT_TYPE_FIELD_CS(STRUCT) copaque
|
#define mSCRIPT_TYPE_FIELD_CS(STRUCT) copaque
|
||||||
#define mSCRIPT_TYPE_FIELD_S_METHOD(STRUCT, NAME) 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_CHARP (&mSTCharPtr)
|
||||||
#define mSCRIPT_TYPE_MS_TABLE (&mSTTable)
|
#define mSCRIPT_TYPE_MS_TABLE (&mSTTable)
|
||||||
#define mSCRIPT_TYPE_MS_WRAPPER (&mSTWrapper)
|
#define mSCRIPT_TYPE_MS_WRAPPER (&mSTWrapper)
|
||||||
|
#define mSCRIPT_TYPE_MS_WEAKREF (&mSTWeakref)
|
||||||
#define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT)
|
#define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT)
|
||||||
#define mSCRIPT_TYPE_MS_CS(STRUCT) (&mSTStructConst_ ## STRUCT)
|
#define mSCRIPT_TYPE_MS_CS(STRUCT) (&mSTStructConst_ ## STRUCT)
|
||||||
#define mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME) (&_mSTStructBindingType_ ## STRUCT ## _ ## NAME)
|
#define mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME) (&_mSTStructBindingType_ ## STRUCT ## _ ## NAME)
|
||||||
|
@ -435,7 +438,8 @@ enum mScriptTypeBase {
|
||||||
mSCRIPT_TYPE_TUPLE,
|
mSCRIPT_TYPE_TUPLE,
|
||||||
mSCRIPT_TYPE_LIST,
|
mSCRIPT_TYPE_LIST,
|
||||||
mSCRIPT_TYPE_TABLE,
|
mSCRIPT_TYPE_TABLE,
|
||||||
mSCRIPT_TYPE_WRAPPER
|
mSCRIPT_TYPE_WRAPPER,
|
||||||
|
mSCRIPT_TYPE_WEAKREF,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum mScriptClassInitType {
|
enum mScriptClassInitType {
|
||||||
|
@ -462,6 +466,7 @@ extern const struct mScriptType mSTString;
|
||||||
extern const struct mScriptType mSTCharPtr;
|
extern const struct mScriptType mSTCharPtr;
|
||||||
extern const struct mScriptType mSTTable;
|
extern const struct mScriptType mSTTable;
|
||||||
extern const struct mScriptType mSTWrapper;
|
extern const struct mScriptType mSTWrapper;
|
||||||
|
extern const struct mScriptType mSTWeakref;
|
||||||
|
|
||||||
struct mScriptTypeTuple {
|
struct mScriptTypeTuple {
|
||||||
size_t count;
|
size_t count;
|
||||||
|
|
|
@ -120,6 +120,33 @@ M_TEST_DEFINE(infoFuncs) {
|
||||||
mScriptContextDeinit(&context);
|
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) {
|
M_TEST_DEFINE(runFrame) {
|
||||||
SETUP_LUA;
|
SETUP_LUA;
|
||||||
CREATE_CORE;
|
CREATE_CORE;
|
||||||
|
@ -206,6 +233,7 @@ M_TEST_DEFINE(memoryWrite) {
|
||||||
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore,
|
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore,
|
||||||
cmocka_unit_test(globals),
|
cmocka_unit_test(globals),
|
||||||
cmocka_unit_test(infoFuncs),
|
cmocka_unit_test(infoFuncs),
|
||||||
|
cmocka_unit_test(detach),
|
||||||
cmocka_unit_test(runFrame),
|
cmocka_unit_test(runFrame),
|
||||||
cmocka_unit_test(memoryRead),
|
cmocka_unit_test(memoryRead),
|
||||||
cmocka_unit_test(memoryWrite),
|
cmocka_unit_test(memoryWrite),
|
||||||
|
|
|
@ -52,11 +52,14 @@ static void _contextFindForFile(const char* key, void* value, void* user) {
|
||||||
void mScriptContextInit(struct mScriptContext* context) {
|
void mScriptContextInit(struct mScriptContext* context) {
|
||||||
HashTableInit(&context->rootScope, 0, (void (*)(void*)) mScriptValueDeref);
|
HashTableInit(&context->rootScope, 0, (void (*)(void*)) mScriptValueDeref);
|
||||||
HashTableInit(&context->engines, 0, _engineContextDestroy);
|
HashTableInit(&context->engines, 0, _engineContextDestroy);
|
||||||
|
TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref);
|
||||||
|
context->nextWeakref = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mScriptContextDeinit(struct mScriptContext* context) {
|
void mScriptContextDeinit(struct mScriptContext* context) {
|
||||||
HashTableDeinit(&context->engines);
|
HashTableDeinit(&context->engines);
|
||||||
HashTableDeinit(&context->rootScope);
|
HashTableDeinit(&context->rootScope);
|
||||||
|
HashTableDeinit(&context->weakrefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mScriptEngineContext* mScriptContextRegisterEngine(struct mScriptContext* context, struct mScriptEngine2* engine) {
|
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) {
|
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);
|
HashTableInsert(&context->rootScope, key, value);
|
||||||
struct mScriptKVPair pair = {
|
struct mScriptKVPair pair = {
|
||||||
.key = key,
|
.key = key,
|
||||||
|
@ -90,8 +99,43 @@ void mScriptContextRemoveGlobal(struct mScriptContext* context, const char* key)
|
||||||
}
|
}
|
||||||
// Since _contextRemoveGlobal doesn't mutate |key|, this cast should be safe
|
// Since _contextRemoveGlobal doesn't mutate |key|, this cast should be safe
|
||||||
HashTableEnumerate(&context->engines, _contextRemoveGlobal, (char*) key);
|
HashTableEnumerate(&context->engines, _contextRemoveGlobal, (char*) key);
|
||||||
|
struct mScriptValue* oldValue = HashTableLookup(&context->rootScope, key);
|
||||||
|
if (oldValue) {
|
||||||
|
mScriptContextClearWeakref(context, oldValue->value.u32);
|
||||||
HashTableRemove(&context->rootScope, key);
|
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) {
|
bool mScriptContextLoadVF(struct mScriptContext* context, const char* name, struct VFile* vf) {
|
||||||
struct mScriptFileInfo info = {
|
struct mScriptFileInfo info = {
|
||||||
|
|
|
@ -212,15 +212,23 @@ struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) {
|
||||||
}
|
}
|
||||||
lua_pop(luaContext->lua, 2);
|
lua_pop(luaContext->lua, 2);
|
||||||
value = lua_touserdata(luaContext->lua, -1);
|
value = lua_touserdata(luaContext->lua, -1);
|
||||||
|
value = mScriptContextAccessWeakref(luaContext->d.context, value);
|
||||||
}
|
}
|
||||||
lua_pop(luaContext->lua, 1);
|
lua_pop(luaContext->lua, 1);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* value) {
|
bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* value) {
|
||||||
|
uint32_t weakref;
|
||||||
|
bool needsWeakref = false;
|
||||||
if (value->type == mSCRIPT_TYPE_MS_WRAPPER) {
|
if (value->type == mSCRIPT_TYPE_MS_WRAPPER) {
|
||||||
value = mScriptValueUnwrap(value);
|
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;
|
bool ok = true;
|
||||||
struct mScriptValue* newValue;
|
struct mScriptValue* newValue;
|
||||||
switch (value->type->base) {
|
switch (value->type->base) {
|
||||||
|
@ -260,7 +268,11 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v
|
||||||
break;
|
break;
|
||||||
case mSCRIPT_TYPE_OBJECT:
|
case mSCRIPT_TYPE_OBJECT:
|
||||||
newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue));
|
newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue));
|
||||||
|
if (needsWeakref) {
|
||||||
|
*newValue = mSCRIPT_MAKE(WEAKREF, weakref);
|
||||||
|
} else {
|
||||||
mScriptValueWrap(value, newValue);
|
mScriptValueWrap(value, newValue);
|
||||||
|
}
|
||||||
luaL_setmetatable(luaContext->lua, "mSTStruct");
|
luaL_setmetatable(luaContext->lua, "mSTStruct");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -484,6 +496,13 @@ int _luaGetObject(lua_State* lua) {
|
||||||
struct mScriptValue* obj = lua_touserdata(lua, -2);
|
struct mScriptValue* obj = lua_touserdata(lua, -2);
|
||||||
struct mScriptValue val;
|
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)) {
|
if (!mScriptObjectGet(obj, key, &val)) {
|
||||||
lua_pop(lua, 2);
|
lua_pop(lua, 2);
|
||||||
lua_pushliteral(lua, "Invalid key");
|
lua_pushliteral(lua, "Invalid key");
|
||||||
|
@ -505,6 +524,12 @@ int _luaSetObject(lua_State* lua) {
|
||||||
struct mScriptValue* obj = lua_touserdata(lua, -3);
|
struct mScriptValue* obj = lua_touserdata(lua, -3);
|
||||||
struct mScriptValue* val = _luaCoerce(luaContext);
|
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);
|
lua_pop(lua, 2);
|
||||||
if (!val) {
|
if (!val) {
|
||||||
lua_pushliteral(lua, "Invalid value");
|
lua_pushliteral(lua, "Invalid value");
|
||||||
|
|
|
@ -195,6 +195,15 @@ const struct mScriptType mSTWrapper = {
|
||||||
.hash = NULL,
|
.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)
|
DEFINE_VECTOR(mScriptList, struct mScriptValue)
|
||||||
|
|
||||||
void _allocTable(struct mScriptValue* val) {
|
void _allocTable(struct mScriptValue* val) {
|
||||||
|
|
Loading…
Reference in New Issue