diff --git a/doc_classes/LuaAPI.xml b/doc_classes/LuaAPI.xml index c5813a62..573b05c1 100644 --- a/doc_classes/LuaAPI.xml +++ b/doc_classes/LuaAPI.xml @@ -45,7 +45,7 @@ - Returns [code]true[/code] only if [code]LuaFunctionName[/code] is defined in current Lua's state as a function. + Returns [code]true[/code] only if [code]LuaFunctionName[/code] is defined in the global environment table as a function. @@ -80,6 +80,21 @@ Will push a copy of a Variant to lua as a global. Returns a error if the type is not supported. + + + + + Will pull a copy of a Variant from lua's registry table. + + + + + + + + Will push a copy of a Variant to lua's registry table. Returns a error if the type is not supported. + + diff --git a/doc_classes/LuaCoroutine.xml b/doc_classes/LuaCoroutine.xml index 7d27b899..af93680a 100644 --- a/doc_classes/LuaCoroutine.xml +++ b/doc_classes/LuaCoroutine.xml @@ -87,6 +87,21 @@ Will push a copy of a Variant to lua as a global. Returns a error if the type is not supported. + + + + + Will pull a copy of a Variant from lua's registry table. + + + + + + + + Will push a copy of a Variant to lua's registry table. Returns a error if the type is not supported. + + diff --git a/src/classes/luaAPI.cpp b/src/classes/luaAPI.cpp index cbbdfb26..a2c69f1f 100644 --- a/src/classes/luaAPI.cpp +++ b/src/classes/luaAPI.cpp @@ -34,6 +34,8 @@ void LuaAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("get_memory_usage"), &LuaAPI::getMemoryUsage); ClassDB::bind_method(D_METHOD("push_variant", "Name", "var"), &LuaAPI::pushGlobalVariant); ClassDB::bind_method(D_METHOD("pull_variant", "Name"), &LuaAPI::pullVariant); + ClassDB::bind_method(D_METHOD("get_registry_value", "Name"), &LuaAPI::getRegistryValue); + ClassDB::bind_method(D_METHOD("set_registry_value", "Name", "var"), &LuaAPI::setRegistryValue); ClassDB::bind_method(D_METHOD("call_function", "LuaFunctionName", "Args"), &LuaAPI::callFunction); ClassDB::bind_method(D_METHOD("call_function_ref", "Args", "LuaFunctionRef"), &LuaAPI::callFunctionRef); ClassDB::bind_method(D_METHOD("function_exists", "LuaFunctionName"), &LuaAPI::luaFunctionExists); @@ -94,6 +96,14 @@ uint64_t LuaAPI::getMemoryLimit() const { return luaAllocData.memoryLimit; } +Variant LuaAPI::getRegistryValue(String name) { + return state.getRegistryValue(name); +} + +Ref LuaAPI::setRegistryValue(String name, Variant var) { + return state.setRegistryValue(name, var); +} + uint64_t LuaAPI::getMemoryUsage() const { return luaAllocData.memoryUsed; } @@ -259,4 +269,4 @@ void *LuaAPI::luaAlloc(void *ud, void *ptr, size_t osize, size_t nsize) { data->memoryUsed -= (uint64_t)osize; data->memoryUsed += (uint64_t)nsize; return memrealloc(ptr, nsize); -} \ No newline at end of file +} diff --git a/src/classes/luaAPI.h b/src/classes/luaAPI.h index 92f04492..510eda10 100644 --- a/src/classes/luaAPI.h +++ b/src/classes/luaAPI.h @@ -48,6 +48,9 @@ class LuaAPI : public RefCounted { Variant callFunction(String functionName, Array args); Variant callFunctionRef(Array args, int funcRef); + Variant getRegistryValue(String name); + Ref setRegistryValue(String name, Variant var); + Ref doFile(String fileName); Ref doString(String code); Ref pushGlobalVariant(String name, Variant var); diff --git a/src/classes/luaCoroutine.cpp b/src/classes/luaCoroutine.cpp index c30b4fbd..c9bf7a60 100644 --- a/src/classes/luaCoroutine.cpp +++ b/src/classes/luaCoroutine.cpp @@ -23,6 +23,8 @@ void LuaCoroutine::_bind_methods() { ClassDB::bind_method(D_METHOD("function_exists", "LuaFunctionName"), &LuaCoroutine::luaFunctionExists); ClassDB::bind_method(D_METHOD("push_variant", "Name", "var"), &LuaCoroutine::pushGlobalVariant); ClassDB::bind_method(D_METHOD("pull_variant", "Name"), &LuaCoroutine::pullVariant); + ClassDB::bind_method(D_METHOD("get_registry_value", "Name"), &LuaCoroutine::getRegistryValue); + ClassDB::bind_method(D_METHOD("set_registry_value", "Name", "var"), &LuaCoroutine::setRegistryValue); // This signal is only meant to be used by await when yield_await is called. ADD_SIGNAL(MethodInfo("coroutine_resume")); @@ -85,6 +87,16 @@ Variant LuaCoroutine::callFunction(String functionName, Array args) { return state.callFunction(functionName, args); } +// Calls LuaState::getRegistryValue() +Variant LuaCoroutine::getRegistryValue(String name) { + return state.getRegistryValue(name); +} + +// Calls LuaState::setRegistryValue() +Ref LuaCoroutine::setRegistryValue(String name, Variant var) { + return state.setRegistryValue(name, var); +} + // loads a string into the threads state Ref LuaCoroutine::loadString(String code) { done = false; diff --git a/src/classes/luaCoroutine.h b/src/classes/luaCoroutine.h index e3f2616b..02f50a1a 100644 --- a/src/classes/luaCoroutine.h +++ b/src/classes/luaCoroutine.h @@ -34,6 +34,9 @@ class LuaCoroutine : public RefCounted { bool luaFunctionExists(String functionName); + Variant getRegistryValue(String name); + Ref setRegistryValue(String name, Variant var); + Ref loadString(String code); Ref loadFile(String fileName); Ref pushGlobalVariant(String name, Variant var); diff --git a/src/luaState.cpp b/src/luaState.cpp index 72d7c49d..aaa0cacc 100644 --- a/src/luaState.cpp +++ b/src/luaState.cpp @@ -43,7 +43,7 @@ lua_State *LuaState::getState() const { #ifndef LAPI_LUAJIT -// Binds lua librares with the lua state +// Binds lua libraries with the lua state void LuaState::bindLibraries(Array libs) { for (int i = 0; i < libs.size(); i++) { String lib = ((String)libs[i]).to_lower(); @@ -85,7 +85,7 @@ void LuaState::bindLibraries(Array libs) { #else -// Binds lua librares with the lua state +// Binds lua libraries with the lua state void LuaState::bindLibraries(Array libs) { for (int i = 0; i < libs.size(); i++) { String lib = ((String)libs[i]).to_lower(); @@ -141,10 +141,52 @@ void LuaState::setHook(Callable hook, int mask, int count) { lua_sethook(L, luaHook, mask, count); } +void LuaState::indexForReading(String name) { +#ifndef LAPI_GDEXTENSION + Vector strs = name.split("."); +#else + PackedStringArray strs = name.split("."); +#endif + for (String str : strs) { + if (lua_type(L, -1) != LUA_TTABLE) { + lua_pop(L, 1); + lua_pushnil(L); + break; + } + lua_getfield(L, -1, str.ascii().get_data()); + lua_remove(L, -2); + } +} + +String LuaState::indexForWriting(String name) { +#ifndef LAPI_GDEXTENSION + Vector strs = name.split("."); +#else + PackedStringArray strs = name.split("."); +#endif + String last = strs[strs.size() - 1]; + strs.remove_at(strs.size() - 1); + for (String str : strs) { + if (lua_type(L, -1) != LUA_TTABLE) { + lua_pop(L, 1); + lua_pushnil(L); + break; + } + lua_getfield(L, -1, str.ascii().get_data()); + lua_remove(L, -2); + } + return last; +} + // Returns true if a lua function exists with the given name bool LuaState::luaFunctionExists(String functionName) { +#ifndef LAPI_LUAJIT + lua_pushglobaltable(L); +#else + lua_pushvalue(L, LUA_GLOBALSINDEX); +#endif + indexForReading(functionName); // LuaJIT does not return a type here - lua_getglobal(L, functionName.ascii().get_data()); int type = lua_type(L, -1); lua_pop(L, 1); return type == LUA_TFUNCTION; @@ -157,20 +199,52 @@ Variant LuaState::getVar(int index) const { // Pull a global variant from Lua to GDScript Variant LuaState::pullVariant(String name) { - lua_getglobal(L, name.ascii().get_data()); +#ifndef LAPI_LUAJIT + lua_pushglobaltable(L); +#else + lua_pushvalue(L, LUA_GLOBALSINDEX); +#endif + indexForReading(name); + Variant val = getVar(-1); + lua_pop(L, 1); + return val; +} +Variant LuaState::getRegistryValue(String name) { + lua_pushvalue(L, LUA_REGISTRYINDEX); + indexForReading(name); Variant val = getVar(-1); lua_pop(L, 1); return val; } +Ref LuaState::setRegistryValue(String name, Variant var) { + lua_pushvalue(L, LUA_REGISTRYINDEX); + String field = indexForWriting(name); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + return LuaError::newError("cannot index nil with string", LuaError::ERR_RUNTIME); // Make it look natural. + } + Ref err = pushVariant(var); + if (err.is_null()) { + lua_setfield(L, -2, field.ascii().get_data()); + lua_pop(L, 1); + return nullptr; + } + lua_pop(L, 1); + return err; +} + // call a Lua function from GDScript Variant LuaState::callFunction(String functionName, Array args) { // push the error handler on to the stack lua_pushcfunction(L, luaErrorHandler); - // put global function name on stack - lua_getglobal(L, functionName.ascii().get_data()); - +#ifndef LAPI_LUAJIT + lua_pushglobaltable(L); +#else + lua_pushvalue(L, LUA_GLOBALSINDEX); +#endif + indexForReading(functionName); // push args for (int i = 0; i < args.size(); ++i) { pushVariant(args[i]); @@ -193,11 +267,23 @@ Ref LuaState::pushVariant(Variant var) const { // Call pushVariant() and set it to a global name Ref LuaState::pushGlobalVariant(String name, Variant var) { +#ifndef LAPI_LUAJIT + lua_pushglobaltable(L); +#else + lua_pushvalue(L, LUA_GLOBALSINDEX); +#endif + String field = indexForWriting(name); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + return LuaError::newError("cannot index nil with string", LuaError::ERR_RUNTIME); // Make it look natural. + } Ref err = pushVariant(var); if (err.is_null()) { - lua_setglobal(L, name.ascii().get_data()); + lua_setfield(L, -2, field.ascii().get_data()); + lua_pop(L, 1); return nullptr; } + lua_pop(L, 1); return err; } @@ -332,7 +418,7 @@ Ref LuaState::pushVariant(lua_State *state, Variant var) { break; } - // If the type being pushed is a lua error, Raise a error + // If the type being pushed is a lua error, Raise an error #ifndef LAPI_GDEXTENSION if (Ref err = Object::cast_to(var.operator Object *()); !err.is_null()) { #else diff --git a/src/luaState.h b/src/luaState.h index b298f970..6c797ff4 100644 --- a/src/luaState.h +++ b/src/luaState.h @@ -29,6 +29,9 @@ class LuaState { Variant pullVariant(String name); Variant callFunction(String functionName, Array args); + Variant getRegistryValue(String name); + Ref setRegistryValue(String name, Variant var); + Ref pushVariant(Variant var) const; Ref pushGlobalVariant(String name, Variant var); Ref handleError(int lua_error) const; @@ -55,6 +58,10 @@ class LuaState { private: lua_State *L = nullptr; + // Helper functions for recursive indexing + void indexForReading(String name); // Puts the object on the stack + String indexForWriting(String name); // Puts the table on the stack and gives the last name. (Please make sure the table is not nil.) + void exposeConstructors(); void createVector2Metatable(); void createVector3Metatable();