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

View File

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

View File

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

View File

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

View File

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

View File

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