Scripting: Add weak references for opaque runtime access

This commit is contained in:
Vicki Pfau 2022-05-08 22:26:00 -07:00
parent 7bb051b01d
commit af44a65c3d
6 changed files with 122 additions and 4 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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),

View File

@ -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) {

View File

@ -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");

View File

@ -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) {